用post构造测试spring bean

时间:2015-07-23 12:39:11

标签: spring junit postconstruct

我有一个类似于此的bean:

@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...
    }
}

问题是在运行测试时PostConstruct之前调用了setUp

3 个答案:

答案 0 :(得分:11)

如果您想编写A单元测试,请不要使用Spring。相反,自己实例化A并传递B的存根/模拟(通过使用构造函数注入或ReflectionTestUtils来设置私有字段)。

例如:

@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
    }

}

如果必须使用Spring,因为要编写集成测试,请使用其他应用程序上下文,用存根/模拟替换B

例如,假设BProduction类中实例化,如下所示:

@Configuration
public class Production {

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

}

为您的测试编写另一个@Configuration课程:

@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;
    }

}

使用@SpringApplicationConfiguration注释在测试中引用它:

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

答案 1 :(得分:1)

在我正在处理的项目中遇到了这个确切的问题,这是我根据问题代码使用的解决方案:

  1. @Autowire在bean中,并@PostConstruct进行测试。
  2. @Before中进行设置。
  3. @PostConstruct末尾显式调用@Before
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, Config.class })
@WebIntegrationTest(randomPort = true)
public class Test {

    // wire in the dependency as well
    @Autowired
    A a;

    @Autowired
    B b;

    @Before
    public void setUp() throws Exception {
        when(b.call(any())).thenReturn("smth");
        
        // "manual" call to @PostConstruct which will now work as expected
        a.setup(); 
    }

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

很显然,您的@PostConstruct方法必须是幂等的,因为它将被调用两次。此外,它还假定默认的单例bean行为。

答案 2 :(得分:0)

另一种选择是自己在测试中实例化应用程序上下文,然后在刷新上下文之前注入模拟,例如:

@Configuration
@ComponentScan
public class TestConfiguration {}
...
ClassToMock mock = mock(ClassToMock.class);
AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext();
c.getDefaultListableBeanFactory().registerResolvableDependency(
        ClassToMock.class,
        mock);
c.register(TestConfiguration.class);
c.refresh();

当上下文中有@PostConstruct个注释并且您希望对模拟先验设置期望时,此替代方法很有用。