我们目前正在使用JUnit和Mockito来测试我们的代码。
我们有一个类似于下面的服务接口。
public interface ServiceA {
void doSomething();
}
它的实现类如下所示。
@Service
@Transactional
public class ServiceAImpl implements ServiceA {
@Inject
private RepositoryA repA;
@Inject
private ShareServiceA sharedServA;
public void doSomething(){
}
}
现在,我想模拟ServiceAImpl类的repA依赖。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {
@Mock
RepositoryA repA;
@InjectMocks
ServiceA servA = new ServiceAImpl();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
........
}
}
调用initMocks后,只启动了ServiceImpl的repA依赖项,并且sharedServA保持为null,因此当被测试的类调用sharedServA方法时会导致null异常。
根据我在互联网和书籍上阅读的一些参考资料,只有在被测试的类具有声明参数的构造函数时才会发生这种情况。但是,对于我上面的例子,我没有声明任何构造函数。这是正确的行为还是我错过了什么?
答案 0 :(得分:2)
你错过了什么,这就是对服务的嘲弄:
@Mock
private ShareServiceA sharedServA;
在您的测试课程中。在
@Mock
RepositoryA repA;
是注入模拟存储库的原因。
编辑:
既然你“不想模仿你的sharedServA”,也许你想要
@Spy
private ServiceAImpl sharedServA;
但这通常是不好的测试风格 - Mockito docs state that:
应该谨慎使用真正的间谍,例如在处理遗留代码时。
答案 1 :(得分:2)
由于你想模拟一个bean但想拥有另一个bean的真实实例,你需要加载整个spring上下文,以便正确注入真正的bean,并且你必须将mocked bean注册到spring上下文中,这样它会被注入。
因此,在您的一个测试配置类中声明了一个RepositoryA
的模拟bean。
@Bean
public void mockedRepositoryA() {
return mock(RepositoryA.class);
}
或强>
在test-context.xml
中如下
<bean id="idOfServiceAHere" class="org.mockito.Mockito" factory-method="mock">
</bean>
然后通过@Inject
注释注入它,就好像它是真正的bean一样。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {
// internally, you will have mocked instance of RepositoryA and
// real instance of ShareServiceA.
@Inject
private ServiceA serviceA;
@Before
public void setUp() throws Exception {
....
}
}
答案 2 :(得分:1)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ServiceAImplTest {
@Mock // not @mock
RepositoryA repA;
// need to mock each dependency of class under test
@Mock
ShareServiceA sharedServA;
// no need to initialize via new
@InjectMocks
ServiceA servA;
@Before
public void setUp() throws Exception {
// no need for this,
// @RunWith(SpringJUnit4ClassRunner.class) will be enough
// MockitoAnnotations.initMocks(this);
........
}
}
查看代码中的评论。
如果您不想模拟ShareServiceA
,您仍然可以模拟它并调用实际方法而不是返回模拟结果。
Mockito.when(sharedServA.someMethod(any())).thenCallRealMethod();
答案 3 :(得分:0)
您应该为ShareServiceA sharedServA创建@Mock 因为java中的任何对象都会创建构造函数默认值。
如果可以的话。请提供有关ShareServiceA的代码。