Spring容器环境下PowerMock单元测试学习开发经验

PowerMock 简介

网上有很多关于PowerMock 的介绍,大家可以自行检索:
https://www.ibm.com/developerworks/cn/java/j-lo-powermock/index.html

首先说明这是一个web开发项目的单元测试中关于PowerMock的使用经验;这个项目的整体架构采用的是SSM框架和经典的web三层开发结构(mapper、service、controller) ;开发中要求所有单元测试覆盖率100%,所以在开始接触并开始学习使用PowerMock。PowerMock 使用的目的就是在单元测试的时候能够解除被测代码中对其他代码的依赖,比如service层中对mapper接口的依赖。

PowerMock 使用方法

1、首先是maven的pom文件中引入PowerMock 依赖的jar包;

		
		    org.powermock
			    powermock-module-junit4
			    1.6.4
			    test
		
		
		    org.powermock
		    powermock-api-mockito
		    1.6.4
		    test
	    

引入jar包依赖后,就可以使用PowerMock 来写测试类程序了:

2、首先在测试类上添加两个注解:@RunWith 和 @ContextConfiguration

// 用于指定junit运行环境,是junit提供给其他框架测试环境接口扩展,为了便于使用spring的依赖注入
@RunWith(SpringJUnit4ClassRunner.class)
// 导入spring的配置文件
@ContextConfiguration({ "classpath:application-context.xml" })

3、然后在测试类中声明要测试类的对象和依赖的对象
@Mock:将被测类中依赖的对象创建为一个mock对象,该对象所有的方法被置空,即都不是真实的方法;
@InjectMocks:创建一个被测类的实例,被@Mock注解标明的mock对象将被注入到该实例中;

	@Mock
	private DepartmentMapper departmentMapper;
	@Autowired
	@InjectMocks // 将mock对象注入到此对象中;此注解必须,否则上边mock不起作用
	private DepartmentManageService departmentManageService;

4、这段代码完成上面两个注解的初始化工作,在每个测试方法执行前都会调用一次(注意和@BeforeClass的区别)

	@Before
	public void setUp()  {
		MockitoAnnotations.initMocks(this);// 初始化 mock注解
	}

在完成初始化之后就可以写测试方法了:

5、在测试方法中,因为我们通过mock产生了一个mock对象解除依赖;但是在被测方法中执行到依赖对象的方法时因为mock对象中所有的方法被置空而无法产生正确的响应;所以我们要定义mock对象的行为;
这里对于有返回值和无返回值void的方法的定义方式不同:
(大家使用时需要将代码中的mock对象和调用的方法替换为自己的mock对象)

//这里 anyObject(),anyInt()是用来匹配mock方法的参数,使用时需引包 import static org.mockito.Matchers.anyObject;
//有返回值的方法
List list = new ArrayList<>(); //定义返回结果
when(departmentMapper.getDepartmentList(anyObject())).thenReturn(list); //调用该方法时就返回list
//无返回值的方法
PowerMockito.doNothing().doThrow(new IllegalArgumentException()).when(departmentMapper).updateDepartmentsById(anyObject());

6、在定义完mock对象的行为后就可以设置被测方法的参数并调用被测方法;然后检测被测方法是否被正确执行;
可以通过以下几种方式检测:

// 通过assertEquals方发比较期望值和实际返回结果是否一致判断是否正确
assertEquals(expected, actual);
//通过 Mockito.verify 判断依赖方法是否被执行以及执行次数和预期是否一致
Mockito.verify(departmentMapper, Mockito.times(2)).getDepartmentList(anyObject());// 被调用2次

最后执行测试方法,通过就没有问题,没通过就要检查测试代码或者被测代码是否有问题了。

遇到的问题

1、mock对象无法注入的问题:
  在完成上面的3,4 步后,理论上应该就可以将依赖对象的mock对象注入到被测对象中了,这种方法在部分测试类中已经成功执行;但是有事也会遇到失败的情况,mock对象无法注入。
  原因:在网上查找原因(菜鸟新手,不懂底层代码实现,只能面向搜索引擎编程);
     有人说是因为PowerMock 对于被测对象中的私有属性无法注入(修改后也不行)
     如果有人知道原因,请帮忙解惑,万分感谢!
  解决方案:幸运的是最后找到了解决方案(可能只适用这里)
      在修改初始化方法,同时取消被测对象的@InjectMocks注解;
既然无法自动注入,我们就采取手动注入的方式解决问题;

@Before
	public void setUp() throws Exception {
		MockitoAnnotations.initMocks(this);// 初始化 mock注解

		// 这里无法将dictmapper注入到 dictService,
		// 可能原因:由于Spring可以使用@Autowired注解对私有的成员进行赋值,此时无法直接对私有的依赖设置mock对象。(不是很理解,因为其他类的单元测试可以)
		// 解决办法:可以通过引入ReflectionTestUtils,解决依赖注入的问题。
		DictService dictService = new DictServiceImpl();
		ReflectionTestUtils.setField(dictService, "dictmapper", dictmapper, DictMapper.class);
	}

2、对于上传文件的方法进行测试:

// MockMultipartFile 是一个专门用于对文件类型进行单元测试时的实现类
File file = new File("文件路径");
MultipartFile mulFile = new MockMultipartFile("product_templet.xlsx", "product_templet.xlsx", ".xlsx", new FileInputStream(file));// 参数:文件名 ,文件在客户机上的文件名,文件类型,文件流

结语

对于单元测试和PowerMock 还有很多内容,这里只对自己在实际开发中遇到的进行记录,相当于本人的学习笔记,希望可以帮到别人,也希望从别人那学到新的东西

你可能感兴趣的:(Spring容器环境下PowerMock单元测试学习开发经验)