0
votes

In my quarkus app, I have repository implementations in a separate gradle project/module, partly because I want to be able to ensure unit tests don't use the database etc.

The trouble is, if I want to use any injected dependencies in test, I need to use @QuarkusTest, and then the build or startup ensures that all dependencies are satisfied.

Is there any way around this that doesn't involve mocking every single external dependency, e.g.?

  • Don't enforce dependencies during build or startup when running tests, and instead leave it to error during runtime if accessed.
  • Some way of mocking all unavailable dependencies (or a subset of them) with a mock that errors on access.
1
I am using weld-junit (and, in the past, cdiunit, but it does not support JUnit 5) together with Mockito. These allow me to run my unit tests in a CDI container, that launches only the beans I need for each specific test (usually the bean under test and everything else is mocked). Another advantage is that they are much, much faster than full integration tests. On the other hand, they use Weld, while Quarkus is using ARC, a limited implementation of CDI, so there may be discrepancies between test and runtime. - Nikos Paraskevopoulos
@NikosParaskevopoulos weld-junit sounds like it would be a great fit. However, I've struggled to get it working, namely it expects injected properties to be private whereas quarkus throws warnings unless the properties are package-private (internal in Kotlin), and it doesn't set up Hibernate/Panache. I've tried to setup an example at github.com/danelowe/quarkus-example/tree/… (the repository test, and the domain/service test error), and I'd be grateful for any working examples. - Dane Lowe
package-private requirement is only for native compilation, however - Dane Lowe
Hi @DaneLowe. I have an example, a few months old. Note I am using Junit 5 (and weld-junit5). Although I like using constructor injection, you can find an example of a bean with property injection and its test. - Nikos Paraskevopoulos
Thanks @NikosParaskevopoulos. That is a great resource and I think those examples would serve as the best answer to the question if you'd like to make that an answer? - Dane Lowe

1 Answers

0
votes

I am using weld-junit to run unit tests on CDI components. In the past I have been using cdiunit too which is great, but it does not support JUnit 5. The idea of both is to launch the tests inside a real CDI container, in a minimal configuration. The important part is that you control explicitly what beans are available for injection, so the system skips the bean discovery part. This makes the tests fairly fast: the CDI overhead is only a few seconds. Both systems allow you to provide mock instances for any dependencies you want - I have been using Mockito for managing the mocks.

The minimal Maven setup (see here is to use a version of the Maven surefire plugin that enables JUnit 5, e.g.:

<pluginManagement>
    <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
    <plugin>
</pluginManagement>

And the other dependencies:

        <dependency>
            <groupId>org.jboss.weld</groupId>
            <artifactId>weld-junit5</artifactId>
            <version>${version.weld-junit5}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${version.junit.jupiter}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${version.junit.jupiter}</version>
            <scope>test</scope>
        </dependency>
        <!-- If you want Mockito: -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>${version.mockito}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>${version.mockito}</version>
            <scope>test</scope>
        </dependency>

Having these in place, you can write & run a test (an example) for your CDI beans (example):

// weld-junit5 annotation - there are other ways to activate weld-junit5
// in your tests, and more features for controlling the container,
// read the full docs!
@EnableAutoWeld
// add real bean implementations in the DI container
@AddBeanClasses(RealBeanDependency.class)
// add extensions if you need
@AddExtensions({Extension1.class, Extension2.class})
// activate scopes, if you need
@ActivateScopes(RequestScoped.class)
// add automatic creation of mocks with Mockito (@Mock)
@ExtendWith(MockitoExtension.class)
public class MyClassTest {
    // an example of introducing mocks to the DI container
    @Produces @Mock
    private ArticleService articleService;

    // injected the "System Under Test"
    // weld-junit will automatically add it to the available beans
    @Inject
    private MyClass sut;

    @Test
    void test() {
        // this runs inside the minimal CDI container
        // read the docs about details, e.g. it starts
        // a fresh container for each test (I think)
    }
}