Home > Software design >  How to mock behaviour of a bean used in @PostConstruct in @SpringBootTest
How to mock behaviour of a bean used in @PostConstruct in @SpringBootTest

Time:08-11

I have been searching for a while and I can't find a solution for this. I need to mock a class that is used in the @PostConstruct of another class. My classes are like this:

@Component
class A {
    private final B b;
    private String s;

    public A(B b) {this.b = b;}

    @PostConstruct
    public void postConstruct() {
        s = b.get().getString().toUpperCase();
    }
}

@Component
class B {
    private final C c;

    public B(C c) {this.c = c;}

    public C get() {return c;}
}

@Component
class C {
    public C() throws Exception {throw new Exception();}

    String getString() {return "C";}
}

Now I create the test that fails because the constructor of C fails:

@SpringBootTest
class DemoApplicationTests {
    @Autowired A a;

    @Test
    public void canLoadContext() {
        assertThat(a).isNotNull();
    }
}

So I try to mock the C class:

@SpringBootTest
class DemoApplicationTests {
    @MockBean
    static C c;

    @Autowired
    A a;

    @BeforeEach
    public void beforeEach() {
        when(c.getString())
            .thenReturn("C_mocked");
    }

    @Test
    public void canLoadContext() {
        assertThat(a).isNotNull();
    }
}

But this way it fails with a NPE in the line s = b.get().getString().toUpperCase(); because the C object in b is not the same that I have mocked with @MockBean. When getString() is called, it returns null and we get a NPE when calling .toUpperCase().

I have also tried to overwrite the mock like this:

    @Configuration
    @AutoConfigureBefore
    static class TestContext {
        @Primary
        public C c() {
            return when(mock(C.class).getString())
                .thenReturn("C_mocked").getMock();
        }
    }

But in this case I get this error:

No qualifying bean of type 'com.example.demo.A' available: expected at least 1 bean which qualifies as autowire candidate.

Could please somebody help out with this?

CodePudding user response:

I don't know how to mark it as duplicate. But please take a look of this https://stackoverflow.com/a/58517289/11538031

CodePudding user response:

The solution remains like this:

@Component
class A {
    private final B b;
    private String s;

    public A(B b) {this.b = b;}

    @PostConstruct
    public void postConstruct() {
        s = b.get().getString().toUpperCase();
    }
}

@Component
class B {
    private final C c;

    public B(C c) {this.c = c;}

    public C get() {return c;}
}

@Component
class C {
    public C() throws Exception {throw new Exception();}

    String getString() {return "C";}
}
@SpringBootTest
class DemoApplicationTests {

    @Autowired
    A a;

    @Test
    public void canLoadContext() {
        assertThat(a).isNotNull();
    }

    @TestConfiguration
    public static class TestContext {
        @Bean
        @Primary
        public C c() {
            return when(mock(C.class).getString())
                .thenReturn("C_mocked").getMock();
        }
    }
}

  • Related