Testing spring bean with post construct

Andy picture Andy · Jul 23, 2015 · Viewed 27.3k times · Source

I have a bean similar to this:

@Service
public class A {

    @Autowired
    private B b;

    @PostConstruct
    public void setup() {
       b.call(param);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, Config.class })
@WebIntegrationTest(randomPort = true)
public class Test {

    @Autowired
    B b;

    @Before
    public void setUp() throws Exception {
        when(b.call(any())).thenReturn("smth");
    }

    @Test
    public void test() throws Exception {
        // test...
    }
}

The problem is that PostConstruct is called before setUp when the test is run.

Answer

hzpz picture hzpz · Jul 23, 2015

If you want to write a unit test of A, then don't use Spring. Instead, instantiate A yourself and pass a stub/mock of B (either by using constructor injection or ReflectionTestUtils to set the private field).

For example:

@Service
public class A {

    private final B b;    

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

    @PostConstruct
    public void setup() {
       b.call(param);
    }
}

-

public class Test {

    @Test
    public void test() throws Exception {
        B b = mock(b);
        A a = new A(b);
        // write some tests for A
    }

}

If you have to use Spring, because you want to write an integration test, use a different application context, where you replace B with a stub/mock.

For example, assuming B is instantiated in a Production class like this:

@Configuration
public class Production {

    @Bean
    public B b() {
        return new B();
    }

}

Write another @Configuration class for your tests:

@Configuration
public class Tests {

    @Bean
    public B b() {
        // using Mockito is just an example
        B b = Mockito.mock(B.class); 
        Mockito.when(b).thenReturn("smth"); 
        return b;
    }

}

Reference it in your test with the @SpringApplicationConfiguration annotation:

@SpringApplicationConfiguration(classes = { Application.class, Tests.class })