springjunitpostconstruct

Testing spring bean with post construct


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.


Solution

  • 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 })