weld / weld-testing

Set of test framework extensions (JUnit 4, JUnit 5, Spock) to enhance the testing of CDI components via Weld. Supports Weld 5.
http://weld.cdi-spec.org/
Apache License 2.0
102 stars 30 forks source link

Better support for @Nested classes #129

Closed JHahnHRO closed 2 years ago

JHahnHRO commented 2 years ago

Currently, if one has a JUnit5 test class with a @WeldSetup WeldInitiator field and a @Nested subclass, the Setup-Field of the enclosing class is not picked up by the nested class. Every nested class seems to need its own Setup-Field.

Example application:

@ApplicationScoped
public class HelloService {

    @Inject
    Helper helper;

    public String sayHello() {
        return "Hello " + helper.getName();
    }
}

@Dependent
public class Helper {

    public String getName() {
        return "World";
    }
}

Example test:

@ExtendWith(WeldJunit5Extension.class)
class HelloServiceTest {

    @WeldSetup
    WeldInitiator weldInitiator = configureWeld();

    private WeldInitiator configureWeld() {
        return WeldInitiator.from(HelloService.class)
                .addBeans(MockBean.of(Mockito.mock(Helper.class), Helper.class))
                .build();
    }

    @Inject
    Helper helper;

    @Inject
    HelloService service;

    @Test
    void testSayHello() {
        when(helper.getName()).thenReturn("Mock");
        final String actual = service.sayHello();
        assertThat(actual).isEqualTo("Hello Mock");
    }

    @Nested
    class NestedTest{

        @WeldSetup
        WeldInitiator weldInitiator = configureWeld(); // (!)

        @Test
        void testSayHelloAgain() {
            when(helper.getName()).thenReturn("nested Mock");
            final String actual = service.sayHello();
            assertThat(actual).isEqualTo("Hello nested Mock");
        }
    }
}

Not including the field marked (!) will lead to a DeploymentException:

WELD-001408: Unsatisfied dependencies for type Helper with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject org.example.HelloServiceTest.helper
  at org.example.HelloServiceTest.helper(HelloServiceTest.java:0)

which is (at least for me) confusing, because there obviously is a CDI-Container running, because it is throwing the DeploymentException. I just assumed that if a container is running, then the @ExtendWith on the enclosing class was picked up, and therefore the @WeldSetup field should be picked up too. Obviously that was not the case.

In hindsight, I can guess what happens under the hood: The extension only looks for fields in the test class and HelloServiceTest.weldInitiator is not a field of HelloServiceTest$NestedTest.

But it would be nice to have this behaviour at least documented. Better yet, the error message could suggest to define an appropriate field for every nested test class. Even better yet: The extension could detect that a test class is nested and check the instance of the enclosing itself.

manovotn commented 2 years ago

Hi and thanks for the report.

I'll add it to my TODO list and look into it but it probably won't be right away. If you have the time and will, I'd be happy to accept a contribution.

From the top of my head, I am not sure how we search for WeldInitiator but I think we are looking only in given test class and then its superclasses. If that's the case, we could just expand the algorithm to look into:

JHahnHRO commented 2 years ago

Yes, I think I can do that. It's also on my TODO list, but it probably won't be right away :-)

EDIT: Surprisingly, I did find some time today. :-) PR see below