OMG,到底在寻找什么..................
(构造一个完美的J2EE系统所需要的完整知识体系)
posts - 198,  comments - 37,  trackbacks - 0

原贴地址:http://www.blogjava.net/ghawk/archive/2005/12/15/24060.html

EasyMock 2.0_ReleaseCandidate 文档翻译

EasyMock 2.0_ReleaseCandidate Readme

Documentation for release 2.0_ReleaseCandidate (October 15 2005)
© 2001-2005 OFFIS, Tammo Freese.

翻译: GHawk , 2005-12-15

EasyMock 2 is a library that provides an easy way to use Mock Objects for given interfaces. EasyMock 2 is available under the terms of the MIT license.

EasyMock 2 是一套用于通过简单的方法对于给定的接口生成 Mock 对象的类库。 EasyMock 2 采用 MIT license 发布。

Mock Objects simulate parts of the behavior of domain code, and are able to check whether they are used as defined. Domain classes can be tested in isolation by simulating their collaborators with Mock Objects.

Mock 对象能够模拟领域对象的部分行为,并且能够检验对领域对象的使用是否与预期的定义一致。领域类通过与 Mock 对象协作,从而获得一个孤立的测试环境。

Writing and maintaining Mock Objects often is a tedious task that may introduce errors. EasyMock 2 generates Mock Objects dynamically - no need to write them, and no generated code!

编写和维护 Mock 对象是一项枯燥而且可能引入错误的工作。 EasyMock 2 能够动态生成 Mock 对象——不需要编写 Mock 对象,也不会产生多余的代码。

EasyMock 2 Benefits      EasyMock 2 的优点

  • Hand-writing classes for Mock Objects is not needed.
  • 不需要人为地编写实现 Mock 对象的类。
  • Supports refactoring-safe Mock Objects: test code will not break at runtime when renaming methods or reordering method parameters
  • 支持“重构安全”的 Mock 对象:当重命名方法和对方法参数重新排序时,测试代码不需要在运行时中断。
  • Supports return values and exceptions.
  • 支持返回值和抛出异常。
  • Supports checking the order of method calls, for one or more Mock Objects.
  • 支持一个或多个 Mock 对象的方法调用顺序检查。

EasyMock 2 Drawbacks    EasyMock 2 的不足

  • EasyMock 2 does only work with Java 2 Version 5.0 and above.
  • EasyMock 2 只能在 Java 5.0 以上的版本中运行。(译注: EasyMock 1.2 可用于低版本的 JDK 。)

EasyMock by default supports the generation of Mock Objects for interfaces only. For those who would like to generate Mock Objects for classes, there is an extension available at the EasyMock home page.

EasyMock 默认只支持为接口生成 Mock 对象。如果需要为类生成 Mock 对象,在 EasyMock 的主页上有扩展包可以实现此功能。

Installation     安装

  1. Java 2 (at least 5.0) is required.
  2. Unzip the EasyMock zip file ( easymock2.0_ReleaseCandidate.zip ). It contains a directory easymock2.0_ReleaseCandidate . Add the EasyMock jar file ( easymock.jar ) from this directory to your classpath.
  1. 首先安装 Java 2(5.0 以上版本 )
  2. 解压缩 EasyMock zip 文件 ( easymock2.0_ReleaseCandidate.zip ) 。其中含有一个目录 easymock2.0_ReleaseCandidate classpath 中加入目录中的 E asyMock jar 文件 ( easymock.jar )

To execute the EasyMock tests, add tests.zip and JUnit (at least 3.8.1) to your class path and start 'java org.easymock.tests.AllTests' .

如果要运行 EasyMock 的测试包,需要将 tests.zip JUnit (至少 3.8.1 版)加入到 classpath 中并且执行“ java org.easymock.tests.AllTests ”。

The source code of EasyMock is stored in the zip file src.zip .

EasyMock 的源码在 src.zip 文件中。

Usage    使用

Most parts of a software system do not work in isolation, but collaborate with other parts to get their job done. In a lot of cases, we do not care about using collaborators in unit testing, as we trust these collaborators. If we do care about it, Mock Objects help us to test the unit under test in isolation. Mock Objects replace collaborators of the unit under test.

软件系统中的大部分组件都需要在协作的环境中工作,而无法在孤立的环境中工作。多数情况下,在单元测试中,我们的侧重点不在被测组建的协作过程上,因为我们已经假设这些协作过程是可信赖的。如果我们同时又要兼顾这些协作过程的测试, Mock 对象能够帮助我们在孤立其他写作对象的环境中对被测对象进行单元测试。 Mock 对象用于替换与被测对象发生协作的对象。

The following examples use the interface Collaborator :

下面是一个使用接口 Collaborator 的例子:

						
								package
						
						
								org.easymock.samples;
				
						
								 
						
				
						
								public
						
						 interface Collaborator {
				
						
								    
								void
								documentAdded(String title);
				
						
								    
								void
								documentChanged(String title);
				
						
								    
								void
								documentRemoved(String title);
				
						
								    
								byte
								voteForRemoval(String title);
				
						
								    
								byte[] voteForRemovals(String[] title);
				
						}
				

Implementors of this interface are collaborators (in this case listeners) of a class named ClassUnderTest :

使用这个接口的是 ClassUnderTest 类,类中的 listener 对象是接口 Collaborator 的一个实例。

						
								public
						
						 class ClassUnderTest {
				
						
								    // ...    
				
						
								    
								public void addListener(Collaborator listener) {
				
						
								        // ... 
				
						
								    }
				
						
								    
								public void addDocument(String title, byte[] document) { 
				
						
								        // ... 
				
						
								    }
				
						
								    
								public
								boolean
								removeDocument(String title) {
				
						
								        // ... 
				
						
								    }
				
						
								    
								public
								boolean
								removeDocuments(String[] titles) {
				
						
								        // ... 
				
						
								    }
				
						}
				

The code for both the class and the interface may be found in the package org.easymock.samples in samples.zip .

以上的类和接口的代码都能在 sample.zip org.easymock.samples 包中找到。

The following examples assume that you are familiar with the JUnit testing framework. Although the tests shown here use JUnit in version 3.8.1, you may as well use JUnit 4 or TestNG.

以下的例子假设您对 JUnit 测试框架比较熟悉。这些测试用的是 JUnit3.8.1 ,但是您也可以使用 JUnit4 TestNG

The first Mock Object   第一个 Mock 对象

We will now build a test case and toy around with it to understand the functionality of the EasyMock package. samples.zip contains a modified version of this test. Our first test should check whether the removal of a non-existing document does not lead to a notification of the collaborator. Here is the test without the definition of the Mock Object:

我们要编写一个测试案例并且围绕着这个案例来理解 EasyMock 的功能。 samples.zip 中有这个测试的修改版本。第一个测试要检查当删除一个不存在的 document 对象时,协作对象 (collaborator) 是否会收到通知。以下是没有定义过 Mock 对象的测试代码:

						
								package
						
						
								org.easymock.samples;
				
						
								 
						
				
						
								import
						
						
								junit.framework.TestCase;
				
						
								 
						
				
						
								public
						
						 class ExampleTest extends TestCase {
				
						
								 
						
				
						
								    
								private
								ClassUnderTest
								classUnderTest;
				
						
								    
								private Collaborator mock;
				
						
								 
						
				
						
								    
								protected void setUp() {
				
						
								        
								
										classUnderTest
								 = new ClassUnderTest();
				
						
								        
								
										classUnderTest.addListener
								
								(mock);
				
						
								    }
				
						
								 
						
				
						
								    
								public void testRemoveNonExistingDocument() {    
				
						
								        // This call should not lead to any notification
				
						
								        // of the Mock Object: 
				
						
								        
								
										classUnderTest.removeDocument
								
								("Does not exist");
				
						
								    }
				
						}
				

For many tests using EasyMock 2, we only need a static import of methods of org.easymock.EasyMock . This is the only non-internal, non-deprecated class of EasyMock 2.

对于使用 EasyMock 2 的大部分测试,我们仅需要对 org.easymock.EasyMock 的方法采用静态引入 (static import 。译注: Java 5 中的新特性。 ) 这是 EasyMock 2 中唯一一个非内部、非不推荐的类。

						
								import
						
						 static org.easymock.EasyMock.*;
				
						
								import
						
						
								junit.framework.TestCase;
				
						
								 
						
				
						
								public
						
						 class ExampleTest extends TestCase {
				
						
								 
						
				
						
								    
								private
								ClassUnderTest
								classUnderTest;
				
						
								    
								private Collaborator mock;
				
						
								    
						
				
						}    
				

To get a Mock Object, we need to

  1. create a Mock Object for the interface we would like to simulate,
  2. record the expected behavior, and
  3. switch the Mock Object to replay state.

要得到一个 Mock 对象,需要以下几步

  1. 为想要模拟的接口创建一个 Mock 对象,
  2. 记录对该对象预期的行为,然后
  3. Mock 对象切换到 replay 状态。

Here is a first example:

以下是范例代码:

						
								    
								protected void setUp() {
				
						
								        
								mock = createMock(Collaborator.class); // 1
				
						
								        
								
										classUnderTest
								 = new ClassUnderTest();
				
						
								        
								
										classUnderTest.addListener
								
								(mock);
				
						
								    }
				
						
								 
						
				
						
								    
								public void testRemoveNonExistingDocument() {
				
						
								        // 2 (we do not expect anything)
				
						
								        
								replay(mock); // 3
				
						
								        
								
										classUnderTest.removeDocument
								
								("Does not exist");
				
						
								    }
				

After activation in step 3, mock is a Mock Object for the Collaborator interface that expects no calls. This means that if we change our ClassUnderTest to call any of the interface's methods, the Mock Object will throw an AssertionError :

在第 3 步,把 Mock 对象激活后, mock 就成为了一个 Collaborator Mock 对象,且不接受任何调用。这意味着如果我们修改 ClassUnderTest 使其调用 Collaborator 接口中的方法, Mock 对象就会抛出 AssertionError

						java.lang.AssertionError: 
				
						
								  Unexpected method call documentRemoved("Does not exist"):
				
						
								    
								at
								org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
				
						
								    
								at
								org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
				
						
								    
								at $Proxy0.documentRemoved(Unknown Source)
				
						
								    
								at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)
				
						
								    
								at
								org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)
				
						
								    
								at
								org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)
				
						
								    ...
				

Adding Behavior    添加行为

Let us write a second test. If a document is added on the class under test, we expect a call to mock.documentAdded ( ) on the Mock Object with the title of the document as argument:

接着,我们再写第 2 个测试。当被测对象中有 document 加入时,预期会调用 Mock 对象的 mock.documentAdded () 方法,并且把 document title 作为调用的参数。

						
								    
						
						
								public
						
						 void testAddDocument() {
				
						
								        
								
										mock.documentAdded
								
								("New Document"); // 2
				
						
								        
								replay(mock); // 3
				
						
								        
								
										classUnderTest.addDocument
								
								("New Document", new byte[0]); 
				
						
								    }
				

So in the record state (before calling replay ), the Mock Object does not behave like a Mock Object, but it records method calls. After calling replay , it behaves like a Mock Object, checking whether the expected method calls are really done.

可见,在 record 状态(调用 replay 之前), Mock 对象不展现 Mock 对象的行为(模拟接口的实现),它仅仅记录方法的调用。在调用 replay 后,它开始以 Mock 对象的行为进行工作,检查预期的方法调用是否真地完成。

If classUnderTest.addDocument ( "New Document", new byte[0]) calls the expected method with a wrong argument, the Mock Object will complain with an AssertionError :

如果 classUnderTest.addDocument (“New Document”, new byte[0]) 以错误的参数调用了预期的方法, Mock 对象将抛出 AssertionError

						java.lang.AssertionError: 
				
						
								  Unexpected method call documentAdded("Wrong title"):
				
						
								    
								
										documentAdded
								
								("New Document"): expected: 1, actual: 0
				
						
								    
								at
								org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
				
						
								    
								at
								org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
				
						
								    
								at $Proxy0.documentAdded(Unknown Source)
				
						
								    
								at
								org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)
				
						
								    
								at
								org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)
				
						
								    
								at
								org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
				
						
								    ...
				

All missed expectations are shown, as well as all fulfilled expectations for the unexpected call (none in this case). If the method call is executed too often, the Mock Object complains, too:

所有没有实现的预期调用会显示出来,满足期望的调用也一起显示(本例中还没有)。如果这个方法调用次数过多(译注:没有按照 replay 前指定的调用次数), Mock 对象也会报错:

						java.lang.AssertionError: 
				
						
								  Unexpected method call documentAdded("New Document"):
				
						
								    
								
										documentAdded
								
								("New Document"): expected: 1, actual: 1 (+1)
				
						
								    
								at
								org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
				
						
								    
								at
								org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
				
						
								    
								at $Proxy0.documentAdded(Unknown Source)
				
						
								    
								at
								org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)
				
						
								    
								at
								org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)
				
						
								    
								at
								org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
				
						
								    ...
				

Verifying Behavior    验证行为

There is one error that we have not handled so far: If we specify behavior, we would like to verify that it is actually used. The current test would pass if no method on the Mock Object is called. To verify that the specified behavior has been used, we have to call verify( mock) :

目前为止,有一个错误我们还未处理:当我们指定了行为,我们需要验证那确实发生了。当前的测试将忽略 Mock 对象没有被调用的情形。为了验证指定的调用行为确实发生了,要调用 verify(mock) 进行验证:

						
								    
						
						
								public
						
						 void testAddDocument() {
				
						
								        
								
										mock.documentAdded
								
								("New Document"); // 2 
				
						
								        
								replay(mock); // 3
				
						
								        
								
										classUnderTest.addDocument
								
								("New Document", new byte[0]);
				
						
								        
								verify(mock);
				
						
								    }
				

If the method is not called on the Mock Object, we now get the following exception:

如果 Mock 对象的方法没有被调用过,将会得到下面的异常:

						java.lang.AssertionError: 
				
						
								  Expectation failure on verify:
				
						
								    
								
										documentAdded
								
								("New Document"): expected: 1, actual: 0
				
						
								    
								at
								org.easymock.internal.MocksControl.verify(MocksControl.java:70)
				
						
								    
								at
								org.easymock.EasyMock.verify(EasyMock.java:536)
				
						
								    
								at
								org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:31)
				
						
								    ...
				

The message of the exception lists all missed expectations.

这些信息显示了没有被预期到的情况。

Expecting an Explicit Number of Calls   期望指定次数的方法调用

Up to now, our test has only considered a single method call. The next test should check whether the addition of an already existing document leads to a call to mock.documentChanged ( ) with the appropriate argument. To be sure, we check this three times (hey, it is an example ;-)):

目前为止,我们的测试只考虑了单个方法的调用。接下来的测试将检验加入一个已经存在的 document 是否会导致 mock.documentChanged () 被正确地调用。为了更确定,我们将检验 3 次(嘿,别见怪,这只是个例子 ;- )):

						
								    
						
						
								public
						
						 void testAddAndChangeDocument() {
				
						
								       
								
										mock.documentAdded
								
								("Document");
				
						
								        
								
										mock.documentChanged
								
								("Document");
				
						
								        
								
										mock.documentChanged
								
								("Document");
				
						
								        
								
										mock.documentChanged
								
								("Document");
				
						
								        
								replay(mock);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								verify(mock);
				
						
								    }
				

To avoid the repetition of mock.documentChanged ( "Document") , EasyMock provides a shortcut. We may specify the call count with the method times( int times) on the object returned by expectLastCall () . The code then looks like:

为了避免(大量的)重复写 mock.documentChanged (“Document”) EasyMock 提供了一个捷径。可以用 expectLastCall () 返回的对象的 times(int times) 方法指定某个方法要被调用的次数。代码如下:

						
								    
						
						
								public
						
						 void testAddAndChangeDocument() {
				
						
								        
								
										mock.documentAdded
								
								("Document");
				
						
								        
								
										mock.documentChanged
								
								("Document");
				
						
								        
								
										expectLastCall
								
								().times(3);
				
						
								        
								replay(mock);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								verify(mock);
				
						
								    }
				

If the method is called too often, we get an exception that tells us that the method has been called too many times. The failure occurs immediately at the first method call exceeding the limit:

如果方法调用次数过多,就会抛出异常通知我们某个方法被过多地调用了。在调用次数一旦超过预期次数时,就会出现这个错误。

						java.lang.AssertionError: 
				
						
								  Unexpected method call documentChanged("Document"):
				
						
								    
								
										documentChanged
								
								("Document"): expected: 3, actual: 3 (+1)
				
						
								         
								at
								org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
				
						
								         
								at
								org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
				
						
								         
								at $Proxy0.documentChanged(Unknown Source)
				
						
								         
								at org.easymock.samples.ClassUnderTest.notifyListenersDocumentChanged(ClassUnderTest.java:67)
				
						
								         
								at
								org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:26)
				
						
								         
								at
								org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
				
						
								    ...
				

If there are too few calls, verify( mock) throws an AssertionError :

如果调用次数太少, verify(mock) 将抛出 AssertionError

						java.lang.AssertionError: 
				
						
								  Expectation failure on verify:
				
						
								    
								
										documentChanged
								
								("Document"): expected: 3, actual: 2
				
						
								         
								at
								org.easymock.internal.MocksControl.verify(MocksControl.java:70)
				
						
								         
								at
								org.easymock.EasyMock.verify(EasyMock.java:536)
				
						
								         
								at
								org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
				
						
								    ...
				

Specifying Return Values   指定返回值

For specifying return values, the object returned by expectLastCall ( ) provides the method andReturn (Object returnValue) . The method has to be called in record state after the call to the Mock Object for which it specifies the return value.

如果要指定方法的返回值,可以使用 expectLastCall () 方法所返回的对象中提供的 andReturn (Object returnValue) 方法。要在 record 状态中指定预期方法的语句后调用该方法。

As an example, we check the workflow for document removal. If ClassUnderTest gets a call for document removal, it asks all collaborators for their vote for removal with calls to byte voteForRemoval(String title) value. Positive return values are a vote for removal. If the sum of all values is positive, the document is removed and documentRemoved ( String title) is called on all collaborators:

举例说明,要检验 document 删除的流程。当 ClassUnderTest 的删除 document 方法被调用时,它将用 collaborators voteForRemoval (String title) 方法查询,进行一次删除的投票操作。正返回值表示投票有效。当投票的总和是正数时, document 即被删除,并调用所有 collaborators 上的 documentRemoved (String title) 进行通知。

						
								    
						
						
								public
						
						 void testVoteForRemoval() {
				
						
								        
								
										mock.documentAdded
								
								("Document");   // expect document addition
				
						
								        
								
										mock.voteForRemoval
								
								("Document");  // expect to be asked to vote ...
				
						
								        
								
										expectLastCall
								
								().andReturn(42);       // ... and vote for it
				
						
								        
								
										mock.documentRemoved
								
								("Document"); // expect document removal
				
						
								        
								replay(mock);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										assertTrue
								
								(
								classUnderTest.removeDocument("Document"));
				
						
								        
								verify(mock);
				
						
								    }
				
						
								 
						
				
						
								    
								public void testVoteAgainstRemoval() {
				
						
								        
								
										mock.documentAdded
								
								("Document");   // expect document addition
				
						
								        
								
										mock.voteForRemoval
								
								("Document");  // expect to be asked to vote ...
				
						
								        
								
										expectLastCall
								
								().andReturn(-42);      // ... and vote against it
				
						
								        
								replay(mock);
				
						
								        
								
										classUnderTest.addDocument
								
								("Document", new byte[0]);
				
						
								        
								
										assertFalse
								
								(
								classUnderTest.removeDocument("Document"));
				
						
								        
								verify(mock);
				
						
								    }
				

Instead of calling expectLastCall ( ) to retrieve the object for setting the return value, there is also a method expect(Object ignored) . It allows to define the expected call and the return value in one line of code. Instead of

作为通过调用 expectLastCall () 来得到对象从而设置返回值的替代,方法 expect(Object ignored) 也能实现相同的功能。它允许在一行中定义预期的方法调用和相应的返回值。

						
								mock.voteForRemoval(
						
						"Document");
				
						
								
										expectLastCall
								
						
						
								(
						
						).andReturn(42);
				

we may use

可以用下面的代码替代:

						
								expect(
						
						
								mock.voteForRemoval
						
						("Document")).andReturn(42);
				

Working with Exceptions    处理异常

For specifying exceptions (more exactly: Throwables) to be thrown, the object returned by expectLastCall ( ) and expect(Object ignored) provides the method andThrow (Throwablethrowable) . The method has to be called in record state after the call to the Mock Object for which it specifies the Throwable to be thrown.

要指定方法调用抛出的异常(更确切地说: Throwables 接口的实例), expectLastCall () expect(Object ignored) 方法的返回对象提供了 andThrow (Throwablethrowable) 实现此功能。此方法需要在 record 状态中指定 Mock 对象预期方法调用后调用,从而指定一个 Throwable 实例的抛出。

Unchecked exceptions (that is, RuntimeException , Error and all their subclasses) can be thrown from every method. Checked exceptions can only be thrown from the methods that do actually throw them.

非受检异常 ( 也就是: RuntimeException , Error 和它们的子类 ) 能够在任何方法中被抛出。受检异常只能在显示声明它们会被抛出的方法中被抛出。

Changing Behavior for the Same Method Call    改变同一个方法调用的行为

It is also possible to specify a changing behavior for a method. The methods times , andReturn , and andThrow may be chained. As an example, we define voteForRemoval ( "Document") to

  • return 42 for the first three calls,
  • throw a RuntimeException for the next four calls,
  • return -42 once.

可以改变一个方法调用的行为。方法的调用次数、 andReturn andThrow 可以串联调用。举例说明,把 voteForRemoval (“Document”) 定义为

  • 3 次调用返回 42
  • 4 次调用抛出 RuntimeException 异常
  • 返回一次 -42
						
								    
								expect(
								mock.voteForRemoval("Document"))
				
						
								        .andReturn(42).times(3)
				
						
								        .andThrow(new RuntimeException(), 4)
				
						
								        .andReturn(-42);
				

Relaxing Call Counts    不严格的调用次数

To relax the expected call counts, there are additional methods that may be used instead of times( int count) :

要以范围的方式定义调用次数,能够使用其他方法代替 times(int count)

times( int min, int max)

to expect between min and max calls,

用上下限指定方法调用次数。

atLeastOnce ()

to expect at least one call, and

指定该方法至少被调用一次。

anyTimes ()

to expected an unrestricted number of calls.

不限制对该方法的调用。

If no call count is specified, one call is expected. If it is requested to make this explicit, once( ) or times(1) may be used.

如果调用次数没有指定,则默认预期调用次数为 1 。如果要显示指定,可以使用方法 once() times(1)

Strict Mocks   严格的 Mocks

On a Mock Object returned by a EasyMock.createMock ( ) , the order of method calls is not checked. If you would like a strict Mock Object that checks the order of method calls, use EasyMock.createStrictMock ( ) to create it.

EasyMock.createMock () 返回的 Mock 对象中,方法调用的先后顺序是不进行检查的。如果要使用对方法调用的先后顺序敏感的 Mock 对象( strict Mock Object ,严格的 Mock 对象),应该使用 EasyMock.createStrictMock () 来创建。

If an unexpected method is called on a strict Mock Object, the message of the exception will show the method calls expected at this point followed by the first conflicting one. verify( mock) shows all missing method calls.

如果非预期地调用了严格的 Mock 对象上的方法,异常信息将在第一个发生冲突的调用前显示该点预期的方法调用。 verify(mock) 显示所有错误的方法调用。

Switching Order Checking On and Off    打开和关闭顺序检查功能

Sometimes, it is necessary to have a Mock Object that checks the order of only some calls. In record phase, you may switch order checking on by calling checkOrder ( mock, true) and switch it off by calling checkOrder (mock, false) .

有时候,有必要仅仅对某些方法调用进行顺序检查。在 record 阶段,可以使用 checkOrder (mock, true) 打开顺序检查功能;用 checkOrder (mock, false) 来关闭顺序检查功能。

There are two differences between a strict Mock Object and a normal Mock Object:

  1. A strict Mock Object has order checking enabled after creation.
  2. A strict Mock Object has order checking enabled after reset (see Reusing a Mock Object).

严格的 Mock 对象和普通的 Mock 对象有以下两处区别:

  1. 严格的 Mock 对象在创建后就打开了顺序检查功能。
  2. 严格的 Mock 对象在复位后也开启顺序检查功能。 ( 参看 Mock 对象的重用 )

Flexible Expectations with Argument Matchers   使用参数匹配器的柔性化预期

To match an actual method call on the Mock Object with an expectation, Object arguments are by default compared with equals() . This may lead to problems. As an example, we consider the following expectation:

要在 Mock 对象上匹配一个预期的方法调用,对象参数默认使用 equals() 方法进行比较。这会引起一些问题。比如:考虑以下的例子:

						
								String[
						
						] documents = new String[] { "Document 1", "Document 2" };
				
						
								expect(
						
						
								mock.voteForRemovals
						
						(documents)).andReturn(42);
				

If the method is called with another array with the same contents, we get an exception, as equals( ) compares object identity for arrays:

如果使用含相同内容的另一个数组对象调用此方法,将出现异常,因为 equals() 比较的是数组对象的 id

						java.lang.AssertionError: 
				
						
								  Unexpected method call voteForRemovals([Ljava.lang.String;@9a029e):
				
						
								    
								
										voteForRemovals
								
								([Ljava.lang.String;@2db19d): expected: 1, actual: 0
				
						
								    
								
										documentRemoved
								
								("Document 1"): expected: 1, actual: 0
				
						
								    
								
										documentRemoved
								
								("Document 2"): expected: 1, actual: 0
				
						
								         
								at
								org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
				
						
								         
								at
								org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
				
						
								         
								at $Proxy0.voteForRemovals(Unknown Source)
				
						
								         
								at
								org.easymock.samples.ClassUnderTest.listenersAllowRemovals(ClassUnderTest.java:88)
				
						
								         
								at
								org.easymock.samples.ClassUnderTest.removeDocuments(ClassUnderTest.java:48)
				
						
								         
								at
								org.easymock.samples.ExampleTest.testVoteForRemovals(ExampleTest.java:83)
				
						
								    ...
				

To specify that only array equality is needed for this call, we may use the method aryEq that is statically imported from the EasyMock class:

为了指定仅对数组的内容进行比较,可以从 EasyMock 类中静态引入和使用 aryEq 方法。

						
								String[
						
						] documents = new String[] { "Document 1", "Document 2" };
				
						
								expect(
						
						
								mock.voteForRemovals
						
						(aryEq(documents))).andReturn(42);
				

If you would like to use matchers in a call, you have to specify matchers for all arguments of the method call.

一旦在方法调用中使用了匹配器,必须对方法中所有的参数使用匹配器。

There are a couple of predefined argument matchers available.

以下是一些预先定义好的匹配器。

eq ( X value)

Matches if the actual value is equals the expected value. Available for all primitive types and for objects.

当实际值和预期值相同时匹配。适用于所有的简单类型和对象。

anyBoolean() , anyByte () , anyChar () , anyDouble () , anyFloat () , anyInt () , anyLong () , anyObject () , anyShort ()

Matches any value. Available for all primitive types and for objects.

匹配任意值。适用于所有的简单类型和对象。

eq ( X value, X delta)

Matches if the actual value is equal to the given value allowing the given delta. Available for float and double.

当实际值在以 value 为基础以 delta 为区间的范围内匹配。适用于 float double

aryEq ( X value)

Matches if the actual value is eaual to the given value according to Arrays.equals ( ) . Available for primitive and object arrays.

当通过 Arrays.equals () 比较返回 true 时匹配。适用于所有的简单类型和对象。

isNull ()

Matches if the actual value is null. Available for objects.

当实际值是 null 时匹配。适用于对象。

notNull ()

Matches if the actual value is not null. Available for objects.

当实际值不为 null 时匹配。适用于对象。

same( X value)

Matches if the actual value is the same as the given value. Available for objects.

当实际值和预期值是同一个对象引用时匹配。适用于对象。

isA ( Class clazz)

Matches if the actual value is an instance of the given class, or if it is in instance of a class that extends or implements the given class. Available for objects.

当实际值的类型与预期类型相同或者与预期类型存在 is-A 关系时匹配。(译注:稍微改了一下,应该不影响理解吧)。适用于对象。

lt ( X value) , leq (X value) , geq (X value) , gt (X value)

Matches if the actual value is less than/less or equal/greter or equal/greater than the given value. Available for all numeric primitive types.

当实际值小于、小于等于、大于等于、大于预期值时匹配。适用于所有数值简单类型。

startsWith ( String prefix), contains(String substring), endsWith(String suffix)

Matches if the actual value starts with/contains/ends with the given value. Available for Strings.

当实际值开始、中间、或结尾处含有期望值时匹配。适用于 String 类型。

matches( String regex), find(String regex)

Matches if the actual value/a substring of the actual value matches the given regular expression. Available for Strings.

当实际值或实际值的一个子字符串与正则表达式匹配时匹配。适用于 String 类型。

and( X first, X second)

Matches if the matchers used in first and second both match. Available for all primitive types and for objects.

当两个匹配器同时匹配时匹配。适用于所有的简单类型和对象。

or( X first, X second)

Matches if one of the matchers used in first and second match. Available for all primitive types and for objects.

当两个匹配器有任何一个匹配时匹配。适用于所有的简单类型和对象。

not( X value)

Matches if the matcher used in value does not match.

当指定的匹配器不匹配时匹配。

Defining own Argument Matchers    定义自己的参数匹配器

Sometimes it is desirable to define own argument matchers. Let's say that an argument matcher is needed that matches an exception if the given exception has the same type and an equal message. It should be used this way:

有时候需要定义自己的参数匹配器。我们假设需要一个匹配器能够对传入的异常(名称和 message )进行匹配。使用方法如下:

						
								    
						
						
								IllegalStateException
						
						 e = new IllegalStateException("Operation not allowed.")
				
						
								    
								expect(
								mock.logThrowable(eqException(e))).andReturn(true);
				

Two steps are necessary to achieve this: The new argument matcher has to be defined, and the static method eqException has to be declared.

实现这一功能需要做两步:定义新的参数匹配器,声明静态方法 eqException

To define the new argument matcher, we implement the interface org.easymock.IArgumentMatcher . This interface contains two methods: matches( Object actual) checks whether the actual argument matches the given argument, and appendTo (StringBuffer buffer) appends a string representation of the argument matcher to the given string buffer. The implementation is straightforward:

要定义新的参数匹配器,要实现 org.easymock.IArgumentMatcher 接口。该接口中有两个方法: matches(Object actual) 检查给定的参数是否与预期的参数相同; appendTo (StringBuffer buffer) 添加一个表示该匹配器的字符串到给定的 StringBuffer 中。实现如下:

						
								import
						
						 org.easymock.IArgumentMatcher;
				
						
								 
						
				
						
								public
						
						 class ThrowableEquals implements IArgumentMatcher {
				
						
								    
								private
								Throwable expected;
				
						
								 
						
				
						
								    
								public
								ThrowableEquals(Throwable expected) {
				
						
								        
								this.expected = expected;
				
						
								    }
				
						
								 
						
				
						
								    
								public
								boolean matches(Object actual) {
				
						
								        
								if (!(actual instanceofThrowable)) {
				
						
								            
								return false;
				
						
								        }
				
						
								        String actualMessage = ((Throwable) actual).getMessage();
				
						
								        
								return
								expected.getClass().equals(actual.getClass())
				
						
								                && expected.getMessage().equals(actualMessage);
				
						
								    }
				
						
								 
						
				
						
								    
								public void appendTo(StringBuffer buffer) {
				
						
								        
								
										buffer.append
								
								("eqException(");
				
						
								        
								
										buffer.append
								
								(
								expected.getClass().getName());
				
						
								        
								
										buffer.append
								
								(" with message \"");
				
						
								        
								
										buffer.append
								
								(
								expected.getMessage());
				
						
								        
								
										buffer.append
								
								("\"")");
				
						
								 
						
				
						
								    }
				
						}
				

The method eqException must create the argument matcher with the given Throwable, report it to EasyMock via the static method reportMatcher ( IArgumentMatcher matcher) , and return a value so that it may be used inside the call (typically 0 , null or false ). A first attempt may look like:

eqException 方法必须为给定的 Throwable 创建参数匹配器,通过静态方法 reportMatcher (IArgumentMatcher matcher) 报告给 EasyMock ,并返回一个在调用中会被使用的值(典型的有: 0 null false )。第一次尝试可能如下:

						
								public
						
						 static ThrowableeqException(Throwable in) {
				
						
								    
								
										EasyMock.reportMatcher
								
								(new ThrowableEquals(in));
				
						
								    
								return null;
				
						}
				

However, this only works if the method logThrowable in the exampe usage accepts Throwable s , and does not require something more specific like a RuntimeException . In the latter case, our code sample would not compile:

然而,这仅在方法 logThrowable 接受 Throwble 的情况下才能工作,并且不需要诸如 RuntimeException 的东西。在后面的例子中,我们的代码无法编译。

						
								    
						
						
								IllegalStateException
						
						 e = new IllegalStateException("Operation not allowed.")
				
						
								    
								expect(
								mock.logThrowable(eqException(e))).andReturn(true);
				

Java 5.0 to the rescue: Instead of defining eqException with a Throwable as parameter and return value, we use a generic type that extends Throwable :

Java 5.0 提供了援助:我们采用继承自 Throwable 的范型类型,而不是把 Throwable 作为 eqException 的参数和返回值。

						
								public
						
						 static <T extends Throwable> T eqException(T in) {
				
						
								    
								
										reportMatcher
								
								(new ThrowableEquals(in));
				
						
								    
								return null;
				
						}
				

Reusing a Mock Object   Mock 对象的重用

Mock Objects may be reset by reset( mock) .

Mock 对象能够通过 reset(mock) 复位。

Using Stub Behavior for Methods    对方法使用 Stub 行为

Sometimes, we would like our Mock Object to respond to some method calls, but we do not want to check how often they are called, when they are called, or even if they are called at all. This stub behavoir may be defined by using the methods andStubReturn ( Object value) , andStubThrow (Throwablethrowable) and asStub () . The following code configures the MockObject to answer 42 to voteForRemoval ( "Document") once and -1 for all other arguments:

有时,我们要 Mock 对象对某些方法调用做出反应,但是我们不需要检查它们被调用的次数。 Stub 行为(也称“哑”行为),可以通过方法 andStubReturn (Object value) andStubThrow (Throwablethrowable) asStub 设置。下面的代码将 Mock 对象设置为在接收到一次 voteForRemoval (“Document”) 调用时返回 42 ,其他情况则返回 -1

						
								    
						
						
								expect(
						
						
								mock.voteForRemoval
						
						("Document")).andReturn(42);
				
						
								    
								expect(
								mock.voteForRemoval(not(eq("Document")))).andStubReturn(-1);
				

Nice Mocks     “好” Mock

On a Mock Object returned by createMock ( ) the default behavior for all methods is to throw an AssertionError for all unexpected method calls. If you would like a "nice" Mock Object that by default allows all method calls and returns appropriate empty values ( 0 , null or false ), use createNiceMock () instead.

使用 createMock () 创建的 Mock 对象对非预期的方法调用默认的行为是抛出 AssertionError ,如果需要一个默认返回 0,null false 等“无效值”的“好”的 Mock 对象(译者注:似乎翻译成“礼貌的” Mock 对象更好些 :-) ),则用 createNiceMock () 方法创建。

Object Methods    Object 类的方法

The behavior for the three object methods equals() , hashCode () and toString () cannot be changed for Mock Objects created with EasyMock, even if they are part of the interface for which the Mock Object is created.

EasyMock 创建的 Mock 对象的 equals() hashCode () toString () 方法的行为是不能改变的,即便在用于创建 Mock 的接口中声明了它们也无法改变他们的行为。

Checking Method Call Order Between Mocks   在多个 Mock 对象间检查方法调用顺序

Up to this point, we have seen a mock object as a single object that is configured by static methods on the class EasyMock . But many of these static methods just identify the hidden control of the Mock Object and delegate to it. A Mock Control is an object implementing the IMocksControl interface.

至此,我们已经对 EasyMock 的静态方法创建的单个 Mock 对象的用法作了介绍。但是绝大部分的静态方法只是展示了 Mock 对象中隐含的控制机制且代理地调用它。 Mock Control 是个实现 IMocksControl 接口的对象。

So instead of

因此,下面这段代码:

						
								    
								IMyInterface mock = createStrictMock(IMyInterface.class);
				
						
								    
								replay(mock);
				
						
								    
								verify(mock); 
				
						
								    
								reset(mock);
				

we may use the equivalent code:

可以用以下的代码等效替代:

						
								    
								IMocksControl ctrl = createStrictControl();
				
						
								    
								IMyInterface mock = ctrl.createMock(IMyInterface.class);
				
						
								  
								  
								
										ctrl.replay
								
								();
				
						
								    
								
										ctrl.verify
								
								(); 
				
						
								    
								
										ctrl.reset
								
								();
				

The IMocksControl allows to create more than one Mock Object, and so it is possible to check the order of method calls between mocks. As an example, we set up two mock objects for the interface IMyInterface , and we expect the calls mock1.a( ) and mock2.a() ordered, then an open number of calls to mock1.c() and mock2.c() , and finally mock2.b() and mock1.b() , in this order:

IMocksControl 允许创建多个 Mock 对象,所以也可以用于检查多个 Mock 对象间的方法调用顺序。举例说明,创建两个 IMyInterface 接口的 Mock 对象,预期调用顺序为 mock1.a() 然后 mock2.a() ,然后自由调用 mock1.c() mock2.c() ,最后以 mock2.b() mock1.b() 结束:

						
								    
						
						
								IMocksControl
						
						 ctrl = createStrictControl();
				
						
								    
								IMyInterface mock1 = ctrl.createMock(IMyInterface.class);
				
						
								    
								IMyInterface mock2 = ctrl.createMock(IMyInterface.class);
				
						
								 
						
				
						
								    
								mock1.a();
				
						
								    
								mock2.a();
				
						
								 
						
				
						
								    
								
										ctrl.checkOrder
								
								(false);
				
						
								 
						
				
						
								    
								mock1.c();
				
						
								    
								
										expectLastCall
								
								().anyTimes();     
				
						
								    
								mock2.c();
				
						
								    
								
										expectLastCall
								
								().anyTimes();     
				
						
								 
						
				
						
								    
								
										ctrl.checkOrder
								
								(true);
				
						
								 
						
				
						
								    
								mock2.b();
				
						
								    
								mock1.b();
				
						
								 
						
				
						
								    
								
										ctrl.replay
								
								();
				

Callbacks   回调

There are a few cases in which it is desirable to run a piece of code whenever a mock object reacts to a method call. For these cases, a Runnable may be provided:

很少有需要在 Mock 对象的方法中有具体的方法运行的场合。如果需要,可以使用 Runnable 。(译者注:不推荐使用过于复杂的 Mock 对象)

						
								  
								  
						
						
								expect(
						
						
								mock.a
						
						()).callback(runnable).andReturn(false);
				

Backward Compatibility     向下兼容

EasyMock 2 contains a compatibility layer so that tests using EasyMock 1.2 for Java 1.5 should work without any modification. The only known differences are visible when failures occur: there are small changes in the failure messages and stack traces, and failures are now reported using Java's AssertionError instead of JUnit's AssertionFailedError .

EasyMock 2 包含了一个兼容层,采用 EasyMock1.2 for Java 5 的测试可以无需修改直接用 EasyMock2 运行。唯一的可见区别是当测试失败时: failure messages stack traces 有微小的不同,并且失败将产生 Java AssertionError 而不是 JUnit AssertionFailedError

EasyMock Development

EasyMock 1.0 has been developed by TammoFreese at OFFIS. The development of EasyMock is now hosted on SourceForge to allow other developers and companies to contribute.

Thanks to the people who gave feedback: NascifAbousalh-Neto, Dave Astels, Francois Beausoleil, George Dinwiddie, Shane Duan, Wolfgang Frech, Steve Freeman, Oren Gross, John D. Heintz, Dale King, Brian Knorr, Dierk Koenig, Chris Kreussling, Robert Leftwich, Johannes Link, Rex Madden, David McIntosh, KarstenMenne, Stephan Mikaty, Ivan Moore, IljaPreuss, Justin Sampson, Richard Scott, Joel Shellman, Shaun Smith, Marco Struck, Ralf Stuckert, Victor Szathmary, Henri Tremblay, Bill Uetrecht, Frank Westphal, Chad Woolley, Bernd Worsch, and numerous others.

Please check the EasyMock home page for new versions, and send bug reports and suggestions to the EasyMock Yahoo!Group. If you would like to subscribe to the EasyMockYahoo!Group, send a message to easymock-subscribe@yahoogroups.com.

EasyMock Version 2.0_ReleaseCandidate (October 15 2005)

Changes since 1.2:

  • support for flexible, refactoring-safe argument matchers
  • no mock control is needed for single Mock Objects
  • stub behavior replaces default behavior
  • support for call order checking for more than one mock, and to switch order checking on and off
  • support for callbacks
  • EasyMock now throws java.lang.AssertionError instead of junit.framework.AssertionFailedError so that it is now independent from the testing framework, you may use it with JUnit 3.8.x, JUnit 4 and TestNG
  • deprecated old API
posted on 2006-08-24 15:46 OMG 阅读(1782) 评论(0)  编辑  收藏 所属分类: <项目>项目测试

只有注册用户登录后才能发表评论。


网站导航:
 

<2006年8月>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿(1)

随笔分类

随笔档案

IT风云人物

文档

朋友

相册

经典网站

搜索

  •  

最新评论

阅读排行榜

评论排行榜