如何用mockito+spring进行单元测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用mockito+spring进行单元测试相关的知识,希望对你有一定的参考价值。

如何用mockito+spring进行单元测试(1)
  本文只说如何使用。
  假设你和我一样,也要构造一个系统,系统中有若干service,假设是A,B,C吧。
  其中A 依赖 B 依赖 C。C是最基本的。
  现在需要对A进行单测,但是C需要依赖于外部环境,而这个环境需要复杂而且不稳定的数据库。怎么办呢?这时你就需要mock掉C。
  第一步你需要在appctx文件中申明:
  <bean id="serviceC" name="service_c_Mock" class="org.mockito.Mockito" factory-method="mock">
  <constructor-arg value="com.x.y.x.ServiceC"></constructor-arg>
  </bean>
  需要注意的是bean的id必须要和A和B中autowire时使用名字一致。或者用类型也行。
  然后在单测case中用@Mock或者@Resource引入sercieC。接着在单测case的Setup中调用如下语句。
  MockitoAnnotations.initMocks(this);
  这时你测试A的方法,A访问B,B访问C时,调用会落在mockito生成的一个代理上。这个时候所调用的任何方法都会返回null。
  这可能不是你期望的,你可能期望在A用到的那些些方法上,输入某个值时返回某个特定的值。这个时候你需要:
  when().then();
  如果这还不够,你希望他输入某个值的时候返回特定值,如果输入的值不是特定值的时候,返回正常逻辑的值,你需要spy,并传入一个serviceC的实现。
  这个方法也解决了那个对于不mock的方法进行返回的问题。
参考技术A 如何用mockito+spring进行单元测试(2)
以前写过一篇blog,写得不是很详细。这次清明节在家好好的整理了下思路,把相关的细节重写下来。很奇怪这些内容在google上找不到,也许是太基本了吧。
为了理解mockito,必须先明白mock测试的原理,它分成以下几个步骤:
建立mock;
将mock和待测试的对象连接起来;
在mock上设置预期的返回值;
开启replay模式,准备记录实际发生的调用;
进行测试;
验证测试结果,调用顺序是否正确,返回值是否符合期望;
本文主要讲第一步和第二步。
对于Mockito而言,有两种方式创建:
1. mock为一个interface提供一个虚拟的实现,
2. spy为object加一个动态代理,实现部分方法的虚拟化。
假设待测试的class声明如下:
public class Svc
DaoInterface dao1;
DaoInstance dao2;

你可以用如下的声明得到一个Interface的mock,或者一个实例的spy,并把它注入到测试对象中:

private Svc svc;
private DaoInterface mockdao;
@Before
public void setup()
mockdao = mock(DaoInterface.class);
svc.setDao1(mockdao);


private Svc svc;
private DaoInstance spydao ;
@Before
public void setup()
tmp= new DaoInstance ();
spydao = spy(tmp);
svc.setDao2(spydao);


请注意到spy和mock的不同。但是spy并不是很好的实践,因为它意味着你的代码不能很好的将变化的部分分离开来。因此最好只在那些历史遗留系统上使用。
上面这种方式并不唯一的途径。为了避免重复代码,Mockito提供了几个注解:
@Mock,被标注的属性是个mock
@Spy,被标注的属性是个spy,需要赋予一个instance
@InjectMocks,将本test中的mock或者spy注入到被标注的属性中,根据构造函数的参数名,或者setter,或者私有属性名。
最后在setup中调用MockitoAnnotations.initMocks(this);就免去了代码的编写。如下所示:

private @InjectMocks Svc svc;
private @Mock DaoInterface dao1;
@Before
public void setup()
//mockdao = mock(DaoInterface.class);
//svc.setDao1(mockdao);
MockitoAnnotations.initMocks(this);


private @InjectMocks Svc svc;
private @Spy DaoInstance dao2=new
DaoInstance() ;
@Before
public void setup()
// tmp= new DaoInstance ();
// spydao = spy(tmp);
// svc.setDao2(spydao);
MockitoAnnotations.initMocks(this);


如果你使用spring,上述代码还可以进一步简化,因为Mockito提供了factory的方法用来创建mock和spy。
正常的bean声明 <bean id=”svc” class=”Svc”>
<bean id=”dao1” class=”…”>
<bean id=”dao2” class=”…”>
mock
<bean id="dao1" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="DaoInterface"></constructor-arg>
</bean>
请注意到svc不变化,mock将自动注入进入。这是因为spring的bean容器,如果id一样,后声明的bean会覆盖前面的bean。

spy
<bean id="daoInst" class="DaoInstance">
</bean>
<bean id="dao2" class="org.mockito.Mockito" factory-method="spy">
<constructor-arg ref="daoInst"></constructor-arg>
</bean>
同样svc不变化,直接注入。请注意spy需要获得一个实例。本回答被提问者采纳

以上是关于如何用mockito+spring进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Mockito 模拟由 Spring 应用程序上下文创建的对象以进行单元测试覆盖?

[翻译]使用Spring Boot进行单元测试

Mockito - JUnit + Mockito 单元测试之打桩 when().thenReturn()

SpringBoot 单元测试利器——Mockito

使用 mockito 对插入方法进行单元测试

mockito单元测试