考拉mock框架

达芬奇密码2018-06-28 18:37

前言

mock在概念大家都不陌生,随着系统依赖增多,需要使用mock的手段来保证单元测试的健壮性,mock的各种好处这里就不用一一赘述了。平常使用的mock框架比如EasyMock,mockito在实际运用中总会有那么一点不方便,为此产生了在这些mock框架基础之上,做一点点小优化的想法。

mockito与haitao-mock对比
mockito示例
package com.netease.kaola.mock.test;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.netease.kaola.mock.test.bean.Father;
import com.netease.kaola.mock.test.bean.Grandson;

/**
 * Created by chenghan on 17/9/18.
 */
public class TypeMockMokitoTest {

	private static final String MOCK_NAME = "mock grandson";

	/**
	 * mockito原生注解,表明待做mock分析的入口类,分析后满足条件的字段会被替换为mock后的实现
	 */
	@InjectMocks
	private Father father = new Father();

	/**
	 * 按照类型做mock,会替换入口类所有类型匹配的字段(包括嵌套类的字段),返回值默认都是null
	 */
	@Mock
	private Grandson grandson;

	@BeforeClass
	public void beforeClass() {
		MockitoAnnotations.initMocks(this);
	}

	@AfterClass
	public void afterClass() {
	}

	@Test
	public void mokito类型匹配mock() {
		String mokitoReturn = "mokito";
		Mockito.when(grandson.name()).thenReturn(mokitoReturn);
		String result = father.getGrandsonOfFather().name();
		Assert.assertTrue(result.equals(mokitoReturn));
		result = father.getSon().getGrandson().name();
		Assert.assertTrue(result.equals(mokitoReturn));
	}
}
mockito可以支持注解方式,按照类型进行注解对象替换,如果我们要替换的对象都很规矩的话,那么到此mockito已经可以基本解决我们的mock需求了。但是,我们想要mock的对象一般都是dubbo  接口,dubbo接口的类型可就不那么“规矩”了,会被代理为形如 com.alibaba.dubbo.common.bytecode.proxyXX”的类型,那么mockito的类型替换就失效了。此外,mockito不支持按照字段名替换,也没办法一键恢复到mock之前的状态。
haitao-mock框架在mockito基础之上做了一下增强优化,对比如下。
按照字段名替换 按照字段类型替换 恢复mock前初始状态
mockito 不支持 支持简单类型替换 不支持
haitao-mock 支持 支持简单类型替换,并且可以识别被代理后的类,比如dubbo service 支持

haitao-mock框架
功能说明
1.兼容mockito所有功能
2.提供注解方式按照字段名进行mock替换
3.提供注解方式按照字段类型替换,ps.包括各种代理类
4.提供一键clear恢复mock之前的状态
使用示例
pom依赖
<dependency>
 <groupId>com.netease.haitao</groupId>
 <artifactId>haitao-mock</artifactId>
 <version>1.0.6</version>
 <scope>test</scope>
</dependency>
初始化
可以选择在父类初始化mock框架,这样一个测试用例类执行完之后可以默认恢复到mock之前的状态,不会和其他用例相互影响。
/**
 * 测试抽象类 Created by chenghan on 17/7/3.
 */
public abstract class AbstractTestClass {

@BeforeClass
 public void beforeClass() {
 // 初始化mock框架
  MockController.mockStart(this);
 }

@AfterClass
 public void afterClass() {
 // 用例执行完,清理并回复到mock之前的场景
  MockController.mockClear(this);
 }

}
按照字段名替换
/**
 * Created by chenghan on 17/7/4.
 */
public class NameMockTest extends AbstractTestClass {

 private static final String MOCK_NAME = "mock grandson";

 /**
 * mockito原生注解,表明待做mock分析的入口类,分析后满足条件的字段会被替换为mock后的实现
 */
 @InjectMocks
 private Father father = new Father();

 /**
 * 按照字段名做mock
 */
 @NameMock(mockFieldName = "grandsonOfFather")
   private Grandson grandson = EasyMockBoostUtils.buildMock(new EasyMockBoostUtils.MockTemplate() {
      public Grandson buildMock() {
         Grandson mockGrandSon = EasyMock.createMock(Grandson.class);
  EasyMock.expect(mockGrandSon.name()).andReturn(MOCK_NAME).anyTimes();
  EasyMock.replay(mockGrandSon);
 return mockGrandSon;
 }
   });

 @Test
 public void 字段名mock() {
      String result = father.getGrandsonOfFather().name();
  Assert.assertTrue(result.equals(MOCK_NAME));
  result = father.getSon().getGrandson().name();
  Assert.assertTrue(!result.equals(MOCK_NAME));
 }
}
按照字段类型替换

/**
 * Created by chenghan on 17/7/4.
 */
public class TypeNestMockTest {

	private static final String MOCK_NAME = "mock grandson";

	/**
	 * mockito原生注解,表明待做mock分析的入口类,分析后满足条件的字段会被替换为mock后的实现
	 */
	@InjectMocks
	private Father father = new Father();

	/**
	 * 按照类型做mock,会替换入口类所有类型匹配的字段(包括嵌套类的字段)
	 */
	@TypeMock
	private Grandson grandson = EasyMockBoostUtils.buildMock(new EasyMockBoostUtils.MockTemplate() {
		public Grandson buildMock() {
			Grandson mockGrandSon = new Grandson();
            Mockito.when(mockGrandSon.name()).thenReturn(MOCK_NAME);
			return mockGrandSon;
		}
	});

	@BeforeClass
	public void beforeClass() {
		MockController.mockStart(this);
	}

	@AfterClass
	public void afterClass() {
		MockController.mockClear(this);
	}

	@Test
	public void haitaoMock类型匹配mock() {
		String result = father.getGrandsonOfFather().name();
		Assert.assertTrue(result.equals(MOCK_NAME));
		result = father.getSon().getGrandson().name();
		Assert.assertTrue(result.equals(MOCK_NAME));
	}
}

本文来自网易实践者社区,经作者程汉授权发布。