`

EasyMock最佳实践

    博客分类:
  • Test
阅读更多
推荐easymock入门贴:
窥探EasyMock(1)基础使用篇
窥探EasyMock(2)进阶使用篇
关于EasyMock常见的几个问题, 这里(http://ozgwei.blogspot.com/2007/06/easymock2-quirk.html)有几点, 我做个翻译:
★EasyMock在录制的时候最典型的写法:
expect(mockEmployeeRepository.findByFirstNameAndLastName("John", "Doe")).andReturn(employees);

★如果有个要录制的方法只知道类型不知道具体的值, 可以这样写:
expect(mockEmployeeRepository.findBySpecification(isA(EmployeeSearchSpecification.class))
.andReturn(employees);


★出现这样的异常:
java.lang.IllegalStateException: 2 matchers expected, 1 recorded.
可能是设置mock方法的期望调用方式时, 既使用了isA的方式来指定参数, 又使用了一个具体值来作为参数
比如这样写:
expect(mockEmployeeRepository.findByDepartmentAndSpecification("HR", 
isA(EmployeeSearchSpecification.class)).andReturn(emplooyees);

正确的写法:
expect(mockEmployeeRepository.findByDepartmentAndSpecification(eq("HR"),
isA(EmployeeSearchSpecification.class)).andReturn(employees);


★andReturn()只是用于当mock对象的方法需要有返回值的情况下,手动设置这个方法的返回值给调用的测试类的。在本例中methodABC (arguments,……)方法就需要在recorder的时候用andReturn()方法指定返回值给TargetClass。如果对于有返回值的方法不指定其返回值,在测试的时候会抛出”java.lang.IllegalStateException: missing behavior definition for the preceeding method call XXX”异常。

★一般不能这样写:
EasyMock.expect(itemPropertyManager.processPublishOrEditSpu(EasyMock.isA(SpuDO.class),
EasyMock.isA(BaseResultDO.class))).andReturn(EasyMock.isA(BaseResultDO.class));

而应该这样写:
EasyMock.expect(itemPropertyManager.processPublishOrEditSpu(EasyMock.isA(SpuDO.class),
EasyMock.isA(BaseResultDO.class))).andReturn(null));

否则会抛出这样的异常:
引用

java.lang.IllegalStateException: matcher calls were not used outside expectations
at org.easymock.internal.RecordState.replay(RecordState.java:72)
at org.easymock.internal.MocksControl.replay(MocksControl.java:57)
at org.easymock.EasyMock.replay(EasyMock.java:1280)

也就是说, 返回值必须给一个具体的值, 而不能只指定返回值类型

★在实用expect来设置mock方法的期望调用方式时, 如果使用到基本类型, 但是又不要基本类型的值, 一般不要这样写:
EasyMock.expect(
					keywordsChecker.checkNormalKeywords(EasyMock
							.isA(String.class), EasyMock.isA(Long.class),
							EasyMock.isA(String.class))).andReturn("");

而应该这样写:
EasyMock.expect(keywordsChecker.checkNormalKeywords(EasyMock.isA(String.class), EasyMock.anyLong(),	
EasyMock.isA(String.class))).andReturn("");


★EasyMock还有一个很让人郁闷的地方, 比如一个方法的参数可能为null, 而你在测试的时候又恰恰传了一个空值, 则这个测试是没法通过的, 会出现类似下面的异常:
引用
java.lang.AssertionError:
  Unexpected method call checkFixKeywords(null, 50010815):
    checkFixKeywords(isA(java.lang.String), <any>): expected: 1, actual: 0
    checkNormalKeywords(isA(java.lang.String), <any>, isA(java.lang.String)): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:45)

这也就要求被测试的方法, 参数不能传递空值.
经过跟踪这个应该是EasyMock的一个bug:
public class InstanceOf implements IArgumentMatcher {

    private final Class<?> clazz;

    public InstanceOf(Class clazz) {
        this.clazz = clazz;
    }

    public boolean matches(Object actual) {
        // 问题在这里:
        return (actual != null) && clazz.isAssignableFrom(actual.getClass());
        // 应该这样写:
        return (actual == null) || clazz.isAssignableFrom(actual.getClass());
    }

    public void appendTo(StringBuffer buffer) {
        buffer.append("isA(" + clazz.getName() + ")");
    }
}


不过这个问题也不是不能绕过, 我用了下面的做法:
		EasyMock.expect(
				keywordsChecker.checkFixKeywords(
						(String) EasyMock.isNull(), EasyMock.anyLong()))
				.andReturn("");
		EasyMock.expect(
				keywordsChecker.checkFixKeywords(
						EasyMock.isA(String.class), EasyMock.anyLong()))
				.andReturn("").anyTimes();

后来我问了一下EasyMock开发者Tammo Freese, 看来不是能算一个bug, 他做了如下的回答:
引用
this is expected behavior, and it is also documented.
The isA() matcher does the same thing as instanceof , so for null, it returns false.

If you would like to match any Object, use
   anyObject()
If you would like to match either Strings or null, use
   or(isA(String.class), isNull())

这里录制了两个expect:将null和非空值分开, 但是第二个的返回值注意加上anyTimes(), 因为我的第二种情况会调用多次, 如果两种情况都会调用多次, 则都加上该方法即可, 否则会出现类似下面的异常:
引用
java.lang.AssertionError:
  Unexpected method call checkFixKeywords("new test", 50010815):
    checkFixKeywords(isA(java.lang.String), <any>): expected: 1, actual: 1 (+1)
    checkNormalKeywords(isA(java.lang.String), <any>, isA(java.lang.String)): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:32)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)


★静态方法是没法用EasyMock进行mock的

★如果需要expect的方法没有返回值, 应该这样写:
先执行mock的要调用的方法, 然后调用EasyMock.expectLastCall().anyTimes();

★如果是对具体类进行mock, 则需要使用org.easymock.classextension.EasyMock去替换org.easymock.EasyMock, 当然这个需要加入对easymockclassextension的引用, 具体使用上则没有区别, 如果采用maven构建工程, 则可以采用下面的写法:
	<!-- mock 相关 -->
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
			<groupId>org.easymock</groupId>
			<artifactId>easymockclassextension</artifactId>
			<version>2.2.1</version>
        </dependency>
        <!-- mock 相关 end-->


★通常对一个方法进行测试的时候, mock一个对象的情况比较多见, 于是我写了下面的一个比较通用的抽象类:
public abstract class AbstractMockExecutor<T> {
	protected T mock;

	/**
	 * @param clazz
	 * @return
	 */
	protected T createMock(Class<T> clazz) {
		mock = EasyMock.createMock(clazz);
		return mock;
	}

	public void execute() throws Exception {
		record();
		EasyMock.replay(mock);
		invoke();
		verify();
	}

	/**
	 * 创建mock对象, 并对要mock的方法进行方法录制, 必须在该方法中手动调用{@link #createMock(Class)}方法.<br>
	 * 该方法的一般做法如下:
	 * 
	 * <pre>
	 * createMock(MockClass);
	 * EasyMock.expect(mock.mockMethod(mockArgument...)).andReturn(
	 * 		mockResult);
	 * BeanObject.setter(mock);
	 * </pre>
	 * 
	 * @throws Exception
	 */
	protected abstract void record() throws Exception;

	/**
	 * 调用经过mock之后的执行过程
	 */
	protected abstract void invoke() throws Exception;

	/**
	 * 验证mock是否正确
	 */
	public void verify() {
		EasyMock.verify(mock);
	}
}


2012-01 更新:
有时候我们会碰到这样的异常信息:
matcher calls were used outside expectations
后来在网上google了一把, 这里有个说明.
简单的说就是andReturn(xxx)中的xxx不能使用EasyMock.anyObject()这样的方法来指定结果. 但是它没有说解决办法. 这里可以利用andAnswer(), 比如这样写:
        final AtomicLong count = new AtomicLong(10000L);
        EasyMock.expect(db.count()).andAnswer(new IAnswer<Long>() {
            @Override
            public Long answer() throws Throwable {
                return count.getAndIncrement();
            }
        }).times(10);



分享到:
评论
4 楼 asterisk622 2014-01-07  
好文好文,你说的问题很多我都碰到过!
不知道你研究过powermock吗? 更加强大的mock扩展


3 楼 tosee 2013-09-03  
    
2 楼 飞天奔月 2013-07-03  
jayung 写道
被浏览1万多次,竟然木有回复?


潜水的比较多
1 楼 jayung 2013-06-20  
被浏览1万多次,竟然木有回复?

相关推荐

    easymock

    《JavaScript模式》的作者Stoyan Stefanov是一位经验丰富、见解独到的前端专家,其作品中提出的众多设计模式和最佳实践,涵盖了函数式编程、对象和类、事件处理、模块化等方面。这些内容不仅为初学者提供了良好的...

    easymock-3.2.zip

    2. **doc文档**:这部分可能包含了项目的用户指南、开发者指南或设计理念等,帮助开发者更深入地理解EasyMock的工作原理和最佳实践。 3. **示例文档**:这些示例通常是一些简单的代码片段,展示了如何在实际项目中...

    EasyMock入门

    EasyMock 是一个强大的Java模拟框架,它允许开发者在单元测试中创建和控制对象的行为,而无需实际实例化这些对象。...同时,不断探索和理解EasyMock的新特性和最佳实践,将进一步提升你的测试能力。

    PowerMock\EasyMock的相关资料和文档

    5. **遵循最佳实践**:遵循单元测试的最佳实践,如使用 Arrange-Act-Assert (AAA) 结构来组织测试代码。 在学习和使用这些工具时,阅读提供的文档非常重要。`EasyMock`的文档会讲解如何创建和配置mock对象,以及...

    easymock教程

    #### 四、最佳实践 ##### 1.16 命名Mock对象 - **命名Mock对象**:为了提高测试代码的可读性,建议给Mock对象起有意义的名字。 ##### 1.17 使用MockControl - **MockControl**:早期版本的Easymock使用...

    easymock教程.zip

    7. **最佳实践**: - 优先使用模拟,而非实际的对象。 - 每个测试只测试一个功能点。 - 清晰地定义和验证每个模拟对象的期望。 - 避免过度使用模拟,保持测试的可读性和维护性。 通过阅读“easymock教程.pdf”...

    easyMcok3.1.jar 与文档

    文档还可能包含常见问题解答、最佳实践和示例代码,帮助开发者更好地理解和使用EasyMock。 在使用EasyMock时,首先需要创建一个模拟对象,例如: ```java Mockery context = new Mockery(); MyInterface mock = ...

    Unitils教程(介绍Unitils的最佳资料)

    Unitils 教程 - 单元测试的最佳实践 Unitils 是一个开源的测试库,它提供了一些实用的工具和方法来帮助开发者编写单元测试。本教程将向您展示如何使用 Unitils 来编写单元测试,并介绍一些常见的单元测试技术。 ...

    单元测试资料

    9. **Best Practices**:文档可能还会包含一些最佳实践,如如何编写有意义的测试用例,避免过度模拟,以及如何有效地组织和维护测试代码。 通过学习这份文档,开发者可以掌握使用Easymock进行单元测试的基本技巧,...

    Unitils示例

    **Unitils 示例** Unitils 是一个强大的 Java 单元测试框架,它提供了丰富的功能来...通过深入理解并应用这些概念,我们可以利用 Unitils 示例工程中的最佳实践,提高测试的质量和效率,确保代码的健壮性和可维护性。

    objenesis.rar

    建议查阅Easymock的官方文档,确认推荐的Objenesis版本,以确保最佳的兼容性和性能。 总的来说,解决`NoClassDefFoundError`的关键在于理解错误的原因——缺少或版本不匹配的依赖,并采取相应的措施添加或更新缺失...

    apache camel java

    Apache Camel 支持基于EIP(Enterprise Integration Patterns)的设计,这些模式是集成领域的最佳实践。EIPs提供了许多预定义的解决常见问题的模板,如内容路由器、消息转换器和错误处理策略,使得开发者能够按照...

    Mock

    在压缩包文件 "mock分享 - 副本.pptx" 中,可能包含了Mock技术的详细讲解、最佳实践以及使用特定工具的教程。PPTX文件可能包含幻灯片,每一页可能涵盖了如下内容: 1. **Mock概念介绍**:解释什么是Mock对象,以及...

    powermock实战手册

    《PowerMock实战手册》 PowerMock是一个强大的...总的来说,《PowerMock实战手册》将帮助开发者深入理解并掌握PowerMock的使用,通过实例解析和最佳实践,提升单元测试的质量和覆盖率,确保代码的健壮性和可维护性。

    JUnit In Action的源代码

    2. **学习最佳实践**:书中可能会涵盖如何使用JUnit与其他库(如Mockito、EasyMock等)配合进行模拟测试,以及如何进行测试驱动开发(TDD)。 3. **熟悉测试工具**:可能包含使用Jenkins、Gradle或Maven等工具集成...

    汪文君powermock实战教学

    11. **总结**:最后,课程会回顾整个PowerMock的学习过程,强调关键点,并给出实际应用中的最佳实践建议。 通过这些实战教程,开发者能够有效地利用PowerMock进行复杂场景的单元测试,提高测试覆盖率,保证代码质量...

    spring 1jar包

    标题 "spring 1jar包" 描述的...这个"spring 1jar包"包含的这些库和框架代表了Java开发中的关键技术和最佳实践,它们共同构建了一个完整的开发环境,使得开发者能够利用Spring的强大功能来构建高效、可维护的应用程序。

    PowerMock学习指南

    **PowerMock学习指南** 在Java开发中,单元测试是一项至关重要的任务,它能...在"PowerMock学习指南.pdf"中,你会找到更多关于如何使用PowerMock的详细步骤和示例,包括如何配置、如何模拟各种情况以及最佳实践建议。

Global site tag (gtag.js) - Google Analytics