读书人

更有效的mock优化spring框架上的moc

发布时间: 2012-10-30 16:13:36 作者: rapoo

更有效的mock,优化spring框架下的mock方法

本文主要是提供了一种解决方案,用于解决spring管理的测试用例在mock过程中,如何有效管理mock宿主和mock实体,并优化mock方法

一、基础类1、Sping配置基础
@ContextConfiguration(locations = { "classpath:spring.xml" })public abstract class BaseServiceTest extendsAbstractTransactionalJUnit4SpringContextTests {/** * 日志器 */protected Logger logger = LoggerFactory.getLogger(getClass());/** * 确认已有异常被抛出 */protected void checkExceptionRaise() {Assert.assertTrue("can't reach here, a exception should be raised",false);}}
?2、通用测试基类
public abstract class BaseTest<T> extends BaseServiceTest {private final Set<MockField> mockFields = new HashSet<MockField>();/** * 既可 mock 接口也可以 mock 类 */protected final Mockery context = new JUnit4Mockery() {{setImposteriser(ClassImposteriser.INSTANCE);}};/** *  */@Afterpublic void restoreField() {context.assertIsSatisfied();for (final MockField mockField : mockFields) {ReflectionTestUtils.setField(mockField.getToMock(),mockField.getFieldName(), mockField.getNativeValue());}}/** * 提取实体类型信息 *  * @return 实体类型信息 */private Class<T> extractGenericParameterInfo() {for (Class<?> current = getClass(); (current != BaseTest.class)|| (current != Object.class); current = current.getSuperclass()) {final Type genericSuperType = current.getGenericSuperclass();if (!(genericSuperType instanceof ParameterizedType)) {continue;}final ParameterizedType genericSuperClass = (ParameterizedType) genericSuperType;final Type[] actualTypes = genericSuperClass.getActualTypeArguments();if (actualTypes.length == 0) {continue;}final Type firstType = actualTypes[0];if (!(firstType instanceof Class)) {continue;}@SuppressWarnings("unchecked")final Class<T> firstClass = (Class<T>) firstType;return firstClass;}throw new IllegalStateException("无法获取有效的模板参数信息");}/** * @return */private Object getMockHost() {Object toMock = getToMock();if (toMock != null) {return toMock;}final Field[] fields = this.getClass().getDeclaredFields();for (final Field field : fields) {final Class<T> genericParameterInfo = extractGenericParameterInfo();if (genericParameterInfo.isAssignableFrom(field.getType())) {Assert.assertNull("重复的mock宿主", toMock);toMock = ReflectionTestUtils.getField(this, field.getName());}}Assert.assertNotNull("mock宿主不能为空", toMock);return toMock;}/** * @return mock宿主 */protected Object getToMock() {return null;}/** * 为本测试用例指定的mock宿主,通过<code>getToMock()</code>指定,设置指定类型的mock实体 *  * @param <E> *            mock实体类型 * @param typeToMock *            用来mock的实体类型 * @return mock实体 */protected <E> E mock(final Class<E> typeToMock) {final E mockObject = context.mock(typeToMock);final Object toMock = getMockHost();String fieldName = null;final Field[] fields = toMock.getClass().getDeclaredFields();for (final Field field : fields) {if (typeToMock.isAssignableFrom(field.getType())) {Assert.assertNull("重复的mock实体", fieldName);fieldName = field.getName();}}Assert.assertNotNull("无法定位mock实体", fieldName);mock(toMock, fieldName, mockObject);return mockObject;}/** * mock对象 *  * @param <E> *            mock对象类型 *  * @param toMock *            mock宿主 * @param fieldName *            属性名称 * @param typeToMock *            用来mock的对象类型 * @return mock对象 */protected <E> E mock(final Object toMock, final String fieldName,final Class<E> typeToMock) {final E mockObject = context.mock(typeToMock);mock(toMock, fieldName, mockObject);return mockObject;}/** * @param toMock *            用来插入mock对象的大对象 * @param fieldName *            属性名称 * @param mockObject *            mock对象 */protected void mock(final Object toMock, final String fieldName,final Object mockObject) {final MockField mockField = new MockField();mockField.setFieldName(fieldName);mockField.setNativeValue(ReflectionTestUtils.getField(toMock, fieldName));mockField.setToMock(toMock);Assert.assertTrue("不允许重复mock", !mockFields.contains(mockField));mockFields.add(mockField);ReflectionTestUtils.setField(toMock, fieldName, mockObject);}/** * 为本测试用例指定的mock宿主,通过<code>getToMock()</code>指定,设置指定类型的mock实体 *  * @param fieldName *            指定的属性名称 *  * @param <E> *            mock实体类型 * @param typeToMock *            用来mock的实体类型 * @return mock实体 */protected <E> E mock(final String fieldName, final Class<E> typeToMock) {final E mockObject = context.mock(typeToMock);mock(getToMock(), fieldName, mockObject);return mockObject;}}
?3、派生自该基类的测试类实例
public class XXXXTest extends BaseTest<XXXXX > {@Autowiredprivate XXXXX xxxxx;@Testpublic void testTTTTTTT() {final YYYYY mockYYYYY = mock(YYYYYY.class);context.checking(new Expectations() {{oneOf(mockYYYYY).doSomething()                                will(returnValue(true))}});Assert.assertTrue(phaseManageBizImpl.TTTTTTT());}}
二、技术难点

1、mock对象的还原,能够使spring的对象不被单个测试用例破坏

??? 如果不剥离注入到mock宿主中的mock实体,下次再次使用该mock宿主时,由于spring不会对bean再次进行初始化,因此第二次使用的mock宿主行为是不可控的。

2、使用反射简化mock接口

三、优势

1、使用@after和@before完成,不需要增加单个测试用例的工作量

2、获取mock实体时方法简单

?

final YYYYY mockYYYYY = mock(YYYYYY.class);

??可以直接mock掉XXXXX中的YYYYY类型变量,不需要指定宿主和实体名字即可完成mock对象注入和剥离

3、能够通过指定属性名进行扩展

??? mock方法有多个overwrite,允许通过指定field那么来注入mock实体

读书人网 >编程

热点推荐