TDD 单元测试 JMock<1>
参考资料:
http://www.jmock.org/cookbook.html JMock的cookbook 使用手册
http://www.ibm.com/developerworks/cn/java/j-lo-testpartten/ 使用设计模式使得单元测试更加简单.
<<Agile Java>> 敏捷开发 TDD思想相关书籍
http://groovy.codehaus.org/Using+JMock+with+Groovy//Groovy 中使用JMock
java JMock 编写单元测试:
导入相关的jar,引入相关的包:
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import static org.hamcrest.Matchers.*;
初始化: 要使用JMock模拟一般的java类时,就需要CGLib的"增强字节码"能力.而不是java一般的动态代理.
context = new JUnit4Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
mock 一般方法[无返回值]:
context.checking(new Expectations() {{
oneOf (subscriber).receive(message);
}});
mock 具有返回值的方法:
//返回一个迭代器.方法无参数
context.checking(new Expectations() {{
oneOf (YourMockClass).mockMethod();
will(returnIterator(testReturnedItreator));
}});
//返回一个对象.方法无参数
context.checking(new Expectations() {{
oneOf (YourMockClass).mockMethod();
will(returnValue(testReturnedItreator));
}});
//返回一个对象,方法参数是任何的String类型
context.checking(new Expectations() {{
oneOf (YourMockClass).mockMethod(with(any(String.class)));
will(returnValue(testReturnedObject));
}});
注意: 上述的oneOf() 已经指定mock对象的mock方法只会被调用一次!!! 如下的示例是能够调用"至少一次",并针对每一次的调用可以指定不同的返回值:
context.checking(new Expectations() {{
atLeast(1).of(dbHelper).getUserById(with(any(String.class)));
will(onConsecutiveCalls(
returnValue(highRiskUser),
returnValue(nomalUser)
));
}});
//..其他类似的方法见:http://www.jmock.org/returning.html
mock 异常的
context.checking(new Expectations() {{
allowing(dbHelper).getUserById(with(any(String.class)));
will(Expectations.throwException(MOCK_SQL_EXCETION)); //模拟一个SQLException的发生.
}});
mock 方法的不同参数,并且返回不同的结果[包括抛出异常情况]
allowing (calculator).add(1,1); will(returnValue(3));
allowing (calculator).add(2,2); will(returnValue(5));
allowing (calculator).sqrt(-1); will(throwException(new IllegalArgumentException());
模拟方法被调用的次数:
one (calculator).load("x"); will(returnValue(10)); //调用一次,
never (calculator).load("y"); //从不调用
mock matchers: 在new Expectations(){....}中, 一般会加入 allowing(...).add(<A>),A区域称为"参数匹配"区域. jmock允许我们做一些模糊的参数匹配,如下:
allowing (calculator).sqrt(with(lessThan(0)); will(throwException(new IllegalArgumentException());
//参数小于0,抛出异常
oneOf (log).append(with(equal(Log.ERROR)), with(stringContaining("sqrt")); //两个参数, 一个equasls(Log.ERROR),另一个是包含了“sqrt”的参数;
oneOf (mock).doSomething(with(aNull(String.class)), aNonNull(String.class)));
oneOf (mock).doSomething(with(eq(1)), with(any(String.class)));
oneOf (mock).doSomething(with(not(eq(1)));
oneOf (mock).doSomething(with(allOf(stringContaining("hello"), stringContaining("world"))));//参数是一个String包括hello,world两个单词
oneOf (mock).doSomething(with(anyOf(stringContains("hello"),stringContains("howdy"))));//参数是一个String包括了hello或者world
mock 匹配方法的调用次数:
one | The invocation is expected once and once only. |
exactly(n).of |
The invocation is expected exactly n times. Note: one is a convenient shorthand for exactly(1) . |
atLeast(n).of | The invocation is expected at least n times. |
atMost(n).of | The invocation is expected at most n times. |
between(min, max).of | The invocation is expected at least min times and at most max times. |
allowing | The invocation is allowed any number of times but does not have to happen. |
ignoring |
The same as allowing . Allowing or ignoring should be chosen to make the test code clearly express intent. |
never | The invocation is not expected at all. This is used to make tests more explicit and so easier to understand. |
mock 忽略相关的mock对象的所有方法,这里的忽略是返回一个"默认"的值:
ignoring (hibernateSessionFactory);
Return Type | "Zero" Value |
---|---|
boolean |
false |
numeric type | zero |
String | "" (empty string) |
Array | Empty array |
Mockable type | A mock that is ignored |
Any other type |
null |