DEV Community

Leanid Herasimau
Leanid Herasimau

Posted on • Originally published at suddo.io on

Quarkus Testing with JUnit 5

Quarkus Testing with JUnit 5

Would you rather discover a faulty foundation when you're putting on the roof, or before you've even laid the first brick? That's unit testing in a nutshell.

  • 🐛 Bug Busting: Catch those pesky bugs early, before they grow into monstrous issues.
  • 🔧 Refactoring Without Fear: Change your code with confidence, knowing your tests have your back.
  • 💎 Code Quality Boost: Write cleaner, more modular code that even your future self will thank you for.

And when it comes to testing microservices with Quarkus? Unit testing becomes your superhero cape. It helps you isolate components, ensuring each piece of your microservice puzzle fits perfectly before you assemble the big picture.

JUnit 5

JUnit 5 isn't just an upgrade; it's a complete overhaul that makes testing in Java feel less like a chore and more like a superpower. Let's break down some of the cool new features that'll make your testing life easier:

What's New and Shiny?

  • @BeforeEach and @AfterEach : Out with the old (@Before and @After), in with the new! These annotations make setting up and tearing down your tests a breeze.
  • @Nested : Group related tests together like a pro. It's like organizing your sock drawer, but for code.
  • @TestFactory : Dynamic tests that adapt and grow. It's testing that evolves with your code!

Let's see a simple test in action:


@Test
void additionShouldWorkLikeInElementarySchool() {
    int result = 2 + 2;
    assertEquals(4, result, "Looks like math is broken. Time to panic!");
}

Enter fullscreen mode Exit fullscreen mode

Simple, right? But don't let its simplicity fool you. This little test packs a punch in ensuring your basic arithmetic isn't going haywire.

Setting Up the Testing Arena in Quarkus

Now, let's get our Quarkus project ready for some serious testing action. First things first, we need to invite JUnit 5 to the party. Add this to your pom.xml:


<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5</artifactId>
    <scope>test</scope>
</dependency>

Enter fullscreen mode Exit fullscreen mode

With that out of the way, let's see how we can make JUnit 5 and Quarkus play nice together:


@QuarkusTest
public class MyAmazingServiceTest {
    @Inject
    MyAmazingService service;

    @Test
    void testServiceLogic() {
        assertTrue(service.isAwesome("Quarkus"), "Our service should recognize Quarkus' awesomeness!");
    }
}

Enter fullscreen mode Exit fullscreen mode

The @QuarkusTest annotation is like a magic wand that sets up the Quarkus test environment. It ensures your tests run in a mini-Quarkus world, complete with dependency injection and all the Quarkus goodies.

REST API Testing: Because APIs Should REST Easy

Testing REST APIs is where things get really fun. We're going to use RestAssured, which makes API testing feel like a walk in the park. Here's a tasty example:


@QuarkusTest
public class SuperheroResourceTest {
    @Test
    void testGetSuperhero() {
        given()
          .when().get("/superhero/batman")
          .then()
             .statusCode(200)
             .body("name", equalTo("Bruce Wayne"))
             .body("superpower", equalTo("Being Rich"));
    }
}

Enter fullscreen mode Exit fullscreen mode

This test is checking if our /superhero endpoint correctly returns Batman's true identity and superpower. Remember, with great power comes great testability!

Pro Tips for API Testing:

  • Test different HTTP methods (GET, POST, PUT, DELETE) to ensure full coverage.
  • Don't forget to test error scenarios. What happens when someone asks for a superhero that doesn't exist?
  • Use parameterized tests to check multiple inputs without duplicating code.

Service Layer Testing: Where the Magic Happens

The service layer is where most of your business logic lives, so it's crucial to test it thoroughly. Here's where Mockito comes in handy:


@QuarkusTest
public class SuperheroServiceTest {
    @InjectMock
    SuperheroRepository mockRepository;

    @Inject
    SuperheroService service;

    @Test
    void testFindSuperhero() {
        Superhero batman = new Superhero("Batman", "Being Rich");
        when(mockRepository.findByName("Batman")).thenReturn(Optional.of(batman));

        Optional<Superhero> result = service.findSuperhero("Batman");
        assertTrue(result.isPresent());
        assertEquals("Being Rich", result.get().getSuperpower());
    }
}

Enter fullscreen mode Exit fullscreen mode

Here, we're mocking the repository to isolate our service test. This way, we're sure we're testing the service logic, not the database interaction.

Avoiding Database Dependence

Remember, unit tests should be fast and independent. Avoid hitting the database in your unit tests. Save that for integration tests. Your future self (and your CI/CD pipeline) will thank you.

Repository Testing: Database Dealings Done Right

When it comes to repository testing, we want to ensure our data access layer works correctly without messing up our actual database. Enter @QuarkusTestResource:


@QuarkusTest
@QuarkusTestResource(H2DatabaseTestResource.class)
public class SuperheroRepositoryTest {
    @Inject
    SuperheroRepository repository;

    @Test
    void testSaveAndRetrieveSuperhero() {
        Superhero wonderWoman = new Superhero("Wonder Woman", "Superhuman Strength");
        repository.persist(wonderWoman);

        Superhero retrieved = repository.findById(wonderWoman.getId()).orElseThrow();
        assertEquals("Wonder Woman", retrieved.getName());
        assertEquals("Superhuman Strength", retrieved.getSuperpower());
    }
}

Enter fullscreen mode Exit fullscreen mode

This setup uses an in-memory H2 database for testing, ensuring your tests are isolated and repeatable.

Best Practices: The Do's and Don'ts of Unit Testing

Do:

  • Keep your tests short and focused. One test, one assertion is a good rule of thumb.
  • Use meaningful test names. test1() tells you nothing, but testSuperheroCreationWithValidInput() speaks volumes.
  • Test edge cases. What happens when your method receives null, an empty string, or a negative number?

Don't:

  • Don't test trivial code. Getters and setters usually don't need tests unless they contain logic.
  • Avoid test interdependence. Each test should be able to run independently.
  • Don't ignore failing tests. A failing test is like a check engine light - ignore it at your peril!

Wrapping Up: The Power of Unit Testing Unleashed

And there you have it, folks! We've journeyed through the land of JUnit 5 and Quarkus, armed with the knowledge to write tests that would make even the most critical code reviewer nod in approval. Remember, good tests are like good friends - they tell you the truth, even when it's not what you want to hear.

By embracing unit testing, you're not just writing better code; you're building a safety net that lets you code with confidence. So go forth, test with gusto, and may your build always be green!

"The only code that's truly bug-free is code that doesn't exist. For everything else, there's unit testing." - Anonymous Developer Who's Seen Things

Happy testing, and may your code be ever bug-free!

Top comments (0)