# Testing components

The component model of Quarkus is built on top CDI. Therefore, Quarkus provides `QuarkusComponentTestExtension` - a JUnit extension that makes it easy to test the components/CDI beans and mock their dependencies. Unlike `@QuarkusTest` this extension does not start a full Quarkus application but merely the CDI container and the configuration service. You can find more details in the Lifecycle section.

| This extension is available in the `quarkus-junit5-component` dependency.

## 1. Basic example

Let’s have a component `Foo` - a CDI bean with two injection points.

`Foo` component

    package org.acme;

    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.inject.Inject;

    @ApplicationScoped (1)
    public class Foo {

        @Inject
        Charlie charlie; (2)

        @ConfigProperty(name = "bar")
        boolean bar; (3)

        public String ping() {
            return bar ? charlie.ping() : "nok";
        }
    }

1 | `Foo` is an `@ApplicationScoped` CDI bean.
2 | `Foo` depends on `Charlie` which declares a method `ping()`.
3 | `Foo` depends on the config property `bar`. `@Inject` is not needed for this injection point because it also declares a CDI qualifier - this is a Quarkus-specific feature.

Then a component test could look like:

Simple component test

    import static org.junit.jupiter.api.Assertions.assertEquals;

    import jakarta.inject.Inject;
    import io.quarkus.test.InjectMock;
    import io.quarkus.test.component.TestConfigProperty;
    import io.quarkus.test.component.QuarkusComponentTest;
    import org.junit.jupiter.api.Test;
    import org.mockito.Mockito;

    @QuarkusComponentTest (1)
    @TestConfigProperty(key = "bar", value = "true") (2)
    public class FooTest {

        @Inject
        Foo foo; (3)

        @InjectMock
        Charlie charlieMock; (4)

        @Test
        public void testPing() {
            Mockito.when(charlieMock.ping()).thenReturn("OK"); (5)
            assertEquals("OK", foo.ping());
        }
    }

1 | The `QuarkusComponentTest` annotation registers the JUnit extension.
2 | Sets a configuration property for the test.
3 | The test injects the component under the test. The types of all fields annotated with `@Inject` are considered the component types under test. You can also specify additional component classes via `@QuarkusComponentTest#value()`. Furthermore, the static nested classes declared on the test class are components too.
4 | The test also injects a mock for `Charlie`. `Charlie` is an unsatisfied dependency for which a synthetic `@Singleton` bean is registered automatically. The injected reference is an "unconfigured" Mockito mock.
5 | We can leverage the Mockito API in a test method to configure the behavior.

`QuarkusComponentTestExtension` also resolves parameters of test methods and injects matching beans.

So the code snippet above can be rewritten as:

Simple component test with test method parameters

    import static org.junit.jupiter.api.Assertions.assertEquals;

    import jakarta.inject.Inject;
    import io.quarkus.test.InjectMock;
    import io.quarkus.test.component.TestConfigProperty;
    import io.quarkus.test.component.QuarkusComponentTest;
    import org.junit.jupiter.api.Test;
    import org.mockito.Mockito;

    @QuarkusComponentTest
    @TestConfigProperty(key = "bar", value = "true")
    public class FooTest {

        @Test
        public void testPing(Foo foo, @InjectMock Charlie charlieMock) { (1)
            Mockito.when(charlieMock.ping()).thenReturn("OK");
            assertEquals("OK", foo.ping());
        }
    }

1 | Parameters annotated with `@io.quarkus.test.component.SkipInject` are never resolved by this extension.

Furthermore, if you need the full control over the `QuarkusComponentTestExtension` configuration then you can use the `@RegisterExtension` annotation and configure the extension programmatically.

The original test could be rewritten like:

Simple component test with programmatic configuration

    import static org.junit.jupiter.api.Assertions.assertEquals;

    import jakarta.inject.Inject;
    import io.quarkus.test.InjectMock;
    import io.quarkus.test.component.QuarkusComponentTestExtension;
    import org.junit.jupiter.api.Test;
    import org.mockito.Mockito;

    public class FooTest {

        @RegisterExtension (1)
        static final QuarkusComponentTestExtension extension = QuarkusComponentTestExtension.builder().configProperty("bar","true").build();

        @Inject
        Foo foo;

        @InjectMock
        Charlie charlieMock;

        @Test
        public void testPing() {
            Mockito.when(charlieMock.ping()).thenReturn("OK");
            assertEquals("OK", foo.ping());
        }
    }

1 | The `QuarkusComponentTestExtension` is configured in a static field of the test class.

## 2. Lifecycle

So what exactly does the `QuarkusComponentTest` do? It starts the CDI container and registers a dedicated configuration object.

If the test instance lifecycle is `Lifecycle#PER_METHOD` (default) then the container is started during the `before each` test phase and stopped during the `after each` test phase. However, if the test instance lifecycle is `Lifecycle#PER_CLASS` then the container is started during the `before all` test phase and stopped during the `after all` test phase.

The fields annotated with `@Inject` and `@InjectMock` are injected after a test instance is created. The parameters of a test method for which a matching bean exists are resolved (unless annotated with `@io.quarkus.test.component.SkipInject` or `@org.mockito.Mock`) when a test method is executed. Finally, the CDI request context is activated and terminated per each test method.

## 3. Injection

Fields of the test class that are annotated with `@jakarta.inject.Inject` and `@io.quarkus.test.InjectMock` are injected after a test instance is created. Furthermore, the parameters of a test method for which a matching bean exists are resolved unless annotated with `@io.quarkus.test.component.SkipInject` or `@org.mockito.Mock`. There are also some JUnit built-in parameters, such as `RepetitionInfo` and `TestInfo`, which are skipped automatically.

An `@Inject` injection point receives the contextual instance of a CDI bean - the real component under test. An `@InjectMock` injection point receives an "unconfigured" Mockito mock that was created for an unsatisfied dependency automatically.

Dependent beans injected into the fields and test method arguments are correctly destroyed before a test instance is destroyed and after the test method completes, respectively.

| Arguments of a `@ParameterizedTest` method that are provided by an `ArgumentsProvider`, for example with `@org.junit.jupiter.params.provider.ValueArgumentsProvider`, must be annotated with `@SkipInject`.

### 3.1. Tested components

The initial set of tested components is derived from the test class:

1. The types of all fields annotated with `@jakarta.inject.Inject` are considered the component types.

2. The types of test methods parameters that are not annotated with `@InjectMock`, `@SkipInject`, or `@org.mockito.Mock` are also considered the component types.

3. If `@QuarkusComponentTest#addNestedClassesAsComponents()` is set to `true` (default) then all static nested classes declared on the test class are components too.

| `@Inject Instance<T>` and `@Inject @All List<T>` injection points are handled specifically. The actual type argument is registered as a component. However, if the type argument is an interface the implementations are not registered automatically.

Additional component classes can be set using `@QuarkusComponentTest#value()` or `QuarkusComponentTestExtensionBuilder#addComponentClasses()`.

### 3.2. Auto Mocking Unsatisfied Dependencies

Unlike in regular CDI environments the test does not fail if a component injects an unsatisfied dependency. Instead, a synthetic bean is registered automatically for each combination of required type and qualifiers of an injection point that resolves to an unsatisfied dependency. The bean has the `@Singleton` scope so it’s shared across all injection points with the same required type and qualifiers. The injected reference is an unconfigured Mockito mock. You can inject the mock in your test using the `io.quarkus.test.InjectMock` annotation and leverage the Mockito API to configure the behavior.

| `@InjectMock` is not intended as a universal replacement for functionality provided by the Mockito JUnit extension. It’s meant to be used for configuration of unsatisfied dependencies of CDI beans. You can use the `QuarkusComponentTest` and `MockitoExtension` side by side.
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.mockito.junit.jupiter.MockitoExtension;

    @ExtendWith(MockitoExtension.class)
    @QuarkusComponentTest
    public class FooTest {

        @TestConfigProperty(key = "bar", value = "true")
        @Test
        public void testPing(Foo foo, @InjectMock Charlie charlieMock, @Mock Ping ping) {
            Mockito.when(ping.pong()).thenReturn("OK");
            Mockito.when(charlieMock.ping()).thenReturn(ping);
            assertEquals("OK", foo.ping());
        }
    }

### 3.3. Custom Mocks For Unsatisfied Dependencies

Sometimes you need the full control over the bean attributes and maybe even configure the default mock behavior. You can use the mock configurator API via the `QuarkusComponentTestExtensionBuilder#mock()` method.

## 4. Configuration

You can set the configuration properties for a test with the `@io.quarkus.test.component.TestConfigProperty` annotation or with the `QuarkusComponentTestExtensionBuilder#configProperty(String, String)` method. If you only need to use the default values for missing config properties, then the `@QuarkusComponentTest#useDefaultConfigProperties()` or `QuarkusComponentTestExtensionBuilder#useDefaultConfigProperties()` might come in useful.

It is also possible to set configuration properties for a test method with the `@io.quarkus.test.component.TestConfigProperty` annotation. However, if the test instance lifecycle is `Lifecycle#_PER_CLASS` this annotation can only be used on the test class and is ignored on test methods.

CDI beans are also automatically registered for all injected Config Mappings. The mappings are populated with the test configuration properties.

## 5. Mocking CDI Interceptors

If a tested component class declares an interceptor binding then you might need to mock the interception too. There are two ways to accomplish this task. First, you can define an interceptor class as a static nested class of the test class.

    import static org.junit.jupiter.api.Assertions.assertEquals;

    import jakarta.inject.Inject;
    import io.quarkus.test.component.QuarkusComponentTest;
    import org.junit.jupiter.api.Test;

    @QuarkusComponentTest
    public class FooTest {

        @Inject
        Foo foo;

        @Test
        public void testPing() {
            assertEquals("OK", foo.ping());
        }

        @ApplicationScoped
        static class Foo {

           @SimpleBinding (1)
           String ping() {
             return "ok";
           }

        }

        @SimpleBinding
        @Interceptor
        static class SimpleInterceptor { (2)

            @AroundInvoke
            Object aroundInvoke(InvocationContext context) throws Exception {
                return context.proceed().toString().toUpperCase();
            }

        }
    }

1 | `@SimpleBinding` is an interceptor binding.
2 | The interceptor class is automatically considered a tested component.

| Static nested classes declared on a test class that is annotated with `@QuarkusComponentTest` are excluded from bean discovery when running a `@QuarkusTest` in order to prevent unintentional CDI conflicts.

The second option is to declare an interceptor method directly in the test class; the method is then invoked in the relevant interception phase.

    import static org.junit.jupiter.api.Assertions.assertEquals;

    import jakarta.inject.Inject;
    import io.quarkus.test.component.QuarkusComponentTest;
    import org.junit.jupiter.api.Test;

    @QuarkusComponentTest
    public class FooTest {

        @Inject
        Foo foo;

        @Test
        public void testPing() {
            assertEquals("OK", foo.ping());
        }

        @SimpleBinding (1)
        @AroundInvoke (2)
        Object aroundInvoke(InvocationContext context) throws Exception {
           return context.proceed().toString().toUpperCase();
        }

        @ApplicationScoped
        static class Foo {

           @SimpleBinding (1)
           String ping() {
             return "ok";
           }

        }
    }

1 | The interceptor bindings of the resulting interceptor are specified by annotating the method with the interceptor binding types.
2 | Defines the interception type.