原URL:http://www.cnblogs.com/zfc2201/archive/2011/12/30/2307970.html
目录
第0章 概述
第1章 jmock初体验
第2章 期望
第3章 返回值
第4章 参数匹配
第5章 指定方法调用次数
第6章 指定执行序列
第7章 状态机
第0章 概述
现在的dev不是仅仅要写code而已,UT已经变为开发中不可缺少的一环。JUnit的出现给javaer的UT编写提供了巨大的便利。但是JUnit并没有解决所有的问题。
当我们要测试一个功能点的时候,需要把不需要我们关注的东西隔离开,从而可以只关注我们需要关注的行为。
jmock通过mock对象来模拟一个对象的行为,从而隔离开我们不关心的其他对象,使得UT的编写变得更为可行,也使得TDD变得更为方便,自然而然的,也就成为敏捷开发的一个利器。
可以到http://www.jmock.org/download.html下载jmock.
添加jar到classpath。
添加的时候,注意把JUnit4的order放到最后。因为junit4它自己带了一个Hamcrest jar。
要是不注意顺序的话,有可能报
java.lang.SecurityException: class "org.hamcrest.TypeSafeMatcher"'s signer information does not match signer information of other classes in the same package。
Note:
这里的类定义用来演示如何使用jmock,所以都是定义为public的。
Java代码
01.public class UserManager {
02.
03. public AddressService addressService;
04.
05. public Address findAddress(String userName) {
06. return addressService.findAddress(userName);
07. }
08.
09. public Iterator<Address> findAddresses(String userName) {
10. return addressService.findAddresses(userName);
11. }
12.}
我们有一个UserManager,要测试它的方法,但是,UserManager是依赖于AddressService的。这里我们准备mock掉AddressService。
第1章 jmock初体验
这个例子的作用在于像一个传统的hello world一样,给大家一个简明的介绍,可以有一个感觉,jmock可以做什么。
AddressService本身太复杂,很难构建,这个时候,jmock出场了。
Java代码
01.@Test
02.public void testFindAddress() {
03.
04. // 建立一个test上下文对象。
05. Mockery context = new Mockery();
06.
07. // 生成一个mock对象
08. final AddressService addressServcie = context
09. .mock(AddressService.class);
10.
11. // 设置期望。
12. context.checking(new Expectations() {
13. {
14. // 当参数为"allen"的时候,addressServcie对象的findAddress方法被调用一次,并且返回西安。
15. oneOf(addressServcie).findAddress("allen");
16. will(returnValue(Para.Xian));
17. }
18. });
19.
20. UserManager manager = new UserManager();
21.
22. // 设置mock对象
23. manager.addressService = addressServcie;
24.
25. // 调用方法
26. Address result = manager.findAddress("allen");
27.
28. // 验证结果
29. Assert.assertEquals(Result.Xian, result);
30.
31.}
那么这里做了什么事情呢?
1 首先,我们建立一个test上下文对象。
2 用这个mockery context建立了一个mock对象来mock AddressService.
3 设置了这个mock AddressService的findAddress应该被调用1次,并且参数为"allen"。
4 生成UserManager对象,设置addressService,调用findAddress。
5 验证期望被满足。
基本上,一个简单的jmock应用大致就是这样一个流程。
最显著的优点就是,我们没有AddressService的具体实现,一样可以测试对AddressService接口有依赖的其他类的行为。也就是说,我们通过mock一个对象来隔离这个对象对要测试的代码的影响。
由于大致的流程是一样的,我们提供一个抽象类来模板化jmock的使用。
Java代码
01.public abstract class TestBase {
02.
03. // 建立一个test上下文对象。
04. protected Mockery context = new Mockery();
05.
06. // 生成一个mock对象
07. protected final AddressService addressServcie = context
08. .mock(AddressService.class);
09.
10. /**
11. * 要测试的userManager.
12. * */
13. protected UserManager manager;
14.
15. /**
16. * 设置UserManager,并且设置mock的addressService。
17. * */
18. private void setUpUserManagerWithMockAddressService() {
19. manager = new UserManager();
20. // 设置mock对象
21. manager.addressService = addressServcie;
22. }
23.
24. /**
25. * 调用findAddress,并且验证返回值。
26. *
27. * @param userName
28. * userName
29. * @param expected
30. * 期望返回的地址。
31. * */
32. protected void assertFindAddress(String userName, Address expected) {
33. Address address = manager.findAddress(userName);
34. Assert.assertEquals(expected, address);
35. }
36.
37. /**
38. * 调用findAddress,并且验证方法抛出异常。
39. * */
40. protected void assertFindAddressFail(String userName) {
41. try {
42. manager.findAddress(userName);
43. Assert.fail();
44. } catch (Throwable t) {
45. // Nothing to do.
46. }
47. }
48.
49. @Test
50. public final void test() {
51.
52. setUpExpectatioin();
53.
54. setUpUserManagerWithMockAddressService();
55.
56. invokeAndVerify();
57. }
58.
59. /**
60. * 建立期望。
61. * */
62. protected abstract void setUpExpectatioin();
63.
64. /**
65. * 调用方法并且验证结果。
66. * */
67. protected abstract void invokeAndVerify();
68.}
这样一来,我们以后的例子中只用关心setUpExpectatioin()和invokeAndVerify()方法就好了。
第2章 期望
好了,让我们来看看一个期望的框架。
Java代码
01.invocation-count (mock-object).method(argument-constraints);
02. inSequence(sequence-name);
03. when(state-machine.is(state-name));
04. will(action);
05. then(state-machine.is(new-state-name));
invocation-count 调用的次数约束
mock-object mock对象
method 方法
argument-constraints 参数约束
inSequence 顺序
when 当mockery的状态为指定的时候触发。
will(action) 方法触发的动作
then 方法触发后设置mockery的状态
这个稍微复杂一些,一下子不明白是正常的,后面讲到其中的细节时,可以回来在看看这个框架。
第3章 返回值
调用一个方法,可以设置它的返回值。即设置will(action)。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. context.checking(new Expectations() {
04. {
05. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
06. allowing(addressServcie).findAddress("allen");
07. will(returnValue(Para.BeiJing));
08.
09. // 当参数为null的时候,抛出IllegalArgumentException异常。
10. allowing(addressServcie).findAddress(null);
11. will(throwException(new IllegalArgumentException()));
12. }
13. });
14.}
15.
16.@Override
17.protected void invokeAndVerify() {
18. assertFindAddress("allen", Result.BeiJing);
19. assertFindAddressFail(null);
20.}
这里演示了两种调用方法的结果,返回值和抛异常。
使用jmock可以返回常量值,也可以根据变量生成返回值。
抛异常是同样的,可以模拟在不同场景下抛的各种异常。
对于Iterator的返回值,jmock也提供了特殊支持。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 生成地址列表
04. final List<Address> addresses = new ArrayList<Address>();
05. addresses.add(Para.Xian);
06. addresses.add(Para.HangZhou);
07.
08. final Iterator<Address> iterator = addresses.iterator();
09.
10. // 设置期望。
11. context.checking(new Expectations() {
12. {
13. // 当参数为"allen"的时候,addressServcie对象的findAddresses方法用returnvalue返回一个Iterator<Address>对象。
14. allowing(addressServcie).findAddresses("allen");
15. will(returnValue(iterator));
16.
17. // 当参数为"dandan"的时候,addressServcie对象的findAddresses方法用returnIterator返回一个Iterator<Address>对象。
18. allowing(addressServcie).findAddresses("dandan");
19. will(returnIterator(addresses));
20. }
21. });
22.
23.}
24.
25.@Override
26.protected void invokeAndVerify() {
27.
28. Iterator<Address> resultIterator = null;
29.
30. // 第1次以"allen"调用方法
31. resultIterator = manager.findAddresses("allen");
32. // 断言返回的对象。
33. assertIterator(resultIterator);
34.
35. // 第2次以"allen"调用方法,返回的与第一次一样的iterator结果对象,所以这里没有next了。
36. resultIterator = manager.findAddresses("allen");
37. Assert.assertFalse(resultIterator.hasNext());
38.
39. // 第1次以"dandan"调用方法
40. resultIterator = manager.findAddresses("dandan");
41. // 断言返回的对象。
42. assertIterator(resultIterator);
43.
44. // 第2次以"dandan"调用方法,返回的是一个全新的iterator。
45. resultIterator = manager.findAddresses("dandan");
46. // 断言返回的对象。
47. assertIterator(resultIterator);
48.}
49.
50./** 断言resultIterator中有两个期望的Address */
51.private void assertIterator(Iterator<Address> resultIterator) {
52. Address address = null;
53. // 断言返回的对象。
54. address = resultIterator.next();
55. Assert.assertEquals(Result.Xian, address);
56. address = resultIterator.next();
57. Assert.assertEquals(Result.HangZhou, address);
58. // 没有Address了。
59. Assert.assertFalse(resultIterator.hasNext());
60.}
从这个例子可以看到对于Iterator,returnValue和returnIterator的不同。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
07. allowing(addressServcie).findAddress("allen");
08. will(new Action() {
09.
10. @Override
11. public Object invoke(Invocation invocation)
12. throws Throwable {
13. return Para.Xian;
14. }
15.
16. @Override
17. public void describeTo(Description description) {
18. }
19. });
20. }
21. });
22.}
23.
24.@Override
25.protected void invokeAndVerify() {
26. assertFindAddress("allen", Result.Xian);
27.}
其实这里要返回一个Action,该Action负责返回调用的返回值。既然知道了这个道理,我们自然可以自定义Action来返回方法调用的结果。
而returnValue,returnIterator,throwException只不过是一些Expectations提供的一些static方法用来方便的构建不同的Action。
除了刚才介绍的
ReturnValueAction 直接返回结果
ThrowAction 抛出异常
ReturnIteratorAction 返回Iterator
还有
VoidAction
ReturnEnumerationAction 返回Enumeration
DoAllAction 所有的Action都执行,但是只返回最后一个Action的结果。
ActionSequence 每次调用返回其Actions列表中的下一个Action的结果。
CustomAction 一个抽象的Action,方便自定义Action。
举个例子来说明DoAllAction和ActionSequence的使用。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // doAllAction
07. allowing(addressServcie).findAddress("allen");
08. will(doAll(returnValue(Para.Xian), returnValue(Para.HangZhou)));
09.
10. // ActionSequence
11. allowing(addressServcie).findAddress("dandan");
12. will(onConsecutiveCalls(returnValue(Para.Xian),
13. returnValue(Para.HangZhou)));
14. }
15. });
16.}
17.
18.@Override
19.protected void invokeAndVerify() {
20. assertFindAddress("allen", Result.HangZhou);
21.
22. assertFindAddress("dandan", Result.Xian);
23. assertFindAddress("dandan", Result.HangZhou);
24.
25.}
第4章 参数匹配
即设置argument-constraints
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
07. allowing(addressServcie).findAddress("allen");
08. will(returnValue(Para.Xian));
09.
10. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
11. allowing(addressServcie).findAddress(with(equal("dandan")));
12. will(returnValue(Para.HangZhou));
13.
14. // 当参数包含"zhi"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. allowing(addressServcie).findAddress(
16. with(new BaseMatcher<String>() {
17.
18. @Override
19. public boolean matches(Object item) {
20. String value = (String) item;
21. if (value == null)
22. return false;
23. return value.contains("zhi");
24. }
25.
26. @Override
27. public void describeTo(Description description) {
28. }
29.
30. }));
31.
32. will(returnValue(Para.BeiJing));
33.
34. // 当参数为其他任何值的时候,addressServcie对象的findAddress方法返回一个Adress对象。
35. allowing(addressServcie).findAddress(with(any(String.class)));
36.
37. will(returnValue(Para.ShangHai));
38. }
39. });
40.
41.}
42.
43.@Override
44.protected void invokeAndVerify() {
45. // 以"allen"调用方法
46. assertFindAddress("allen", Result.Xian);
47. // 以"dandan"调用方法
48. assertFindAddress("dandan", Result.HangZhou);
49. // 以包含"zhi"的参数调用方法
50. assertFindAddress("abczhidef", Result.BeiJing);
51. // 以任意一个字符串"abcdefg"调用方法
52. assertFindAddress("abcdefg", Result.ShangHai);
53.}
测试演示了直接匹配,equal匹配,自定义匹配,任意匹配。
其实,这些都是为了给参数指定一个Matcher,来决定调用方法的时候,是否接收这个参数。
在Expectations中提供了一些便利的方法方便我们构造Matcher.
其中
equal判断用equal方法判断是否相等。
same判断是否是同一个引用。
any,anything接收任意值。
aNull接收null。
aNonNull接收非null.
jmock提供了很多有用的匹配。可以用来扩展写出更多的Matcher。
基本Matcher
IsSame 引用相等。
IsNull
IsInstanceOf
IsEqual 考虑了数组的相等(长度相等,内容equals)
IsAnything always return true.
逻辑Matcher
IsNot
AnyOf
AllOf
其他
Is 装饰器模式的Matcher,使得可读性更高。
第5章 指定方法调用次数
可以指定方法调用的次数。即对invocation-count进行指定。
exactly 精确多少次
oneOf 精确1次
atLeast 至少多少次
between 一个范围
atMost 至多多少次
allowing 任意次
ignoring 忽略
never 从不执行
可以看出,这些range都是很明了的。只有allowing和ignoring比较特殊,这两个的实际效果是一样的,但是关注点不一样。当我们允许方法可以任意次调用时,用allowing,当我们不关心一个方法的调用时,用ignoring。
第6章 指定执行序列
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final Sequence sequence = context.sequence("mySeq_01");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
10. oneOf(addressServcie).findAddress("allen");
11. inSequence(sequence);
12. will(returnValue(Para.Xian));
13.
14. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. oneOf(addressServcie).findAddress("dandan");
16. inSequence(sequence);
17. will(returnValue(Para.HangZhou));
18.
19. }
20. });
21.
22.}
23.
24.@Override
25.protected void invokeAndVerify() {
26. assertFindAddress("allen", Result.Xian);
27. assertFindAddress("dandan", Result.HangZhou);
28.}
这里指定了调用的序列。使得调用必须以指定的顺序调用。
来看一个反例
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final Sequence sequence = context.sequence("mySeq_01");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
10. oneOf(addressServcie).findAddress("allen");
11. inSequence(sequence);
12. will(returnValue(Para.Xian));
13.
14. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. oneOf(addressServcie).findAddress("dandan");
16. inSequence(sequence);
17. will(returnValue(Para.HangZhou));
18.
19. }
20. });
21.}
22.
23.@Override
24.protected void invokeAndVerify() {
25. assertFindAddressFail("dandan");
26.}
当指定序列的第一个调用没有触发的时候,直接调用第2个,则会抛异常。
Note:指定序列的时候注意方法调用次数这个约束,如果是allowing那么在这个序列中,它是可以被忽略的。
第7章 状态机
状态机的作用在于模拟对象在什么状态下调用才用触发。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final States states = context.states("sm").startsAs("s1");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 状态为s1参数包含allen的时候返回西安
10. allowing(addressServcie).findAddress(
11. with(StringContains.containsString("allen")));
12. when(states.is("s1"));
13. will(returnValue(Para.Xian));
14.
15. // 状态为s1参数包含dandan的时候返回杭州,跳转到s2。
16. allowing(addressServcie).findAddress(
17. with(StringContains.containsString("dandan")));
18. when(states.is("s1"));
19. will(returnValue(Para.HangZhou));
20. then(states.is("s2"));
21.
22. // 状态为s2参数包含allen的时候返回上海
23. allowing(addressServcie).findAddress(
24. with(StringContains.containsString("allen")));
25. when(states.is("s2"));
26. will(returnValue(Para.ShangHai));
27. }
28. });
29.}
30.
31.@Override
32.protected void invokeAndVerify() {
33. // s1状态
34. assertFindAddress("allen", Result.Xian);
35. assertFindAddress("allen0", Result.Xian);
36.
37. // 状态跳转到 s2
38. assertFindAddress("dandan", Result.HangZhou);
39.
40. // s2状态
41. assertFindAddress("allen", Result.ShangHai);
42.}
可以看到,如果序列一样,状态也为期望的执行设置了约束,这里就是用状态来约束哪个期望应该被执行。
可以用is或者isNot来限制状态。
状态机有一个很好的用处。
当我们建立一个test执行上下文的时候,如果建立的时候和执行的时候,我们都需要调用mock ojbect的方法,那么我们可以用状态机把这两部分隔离开。让他们在不同的状态下执行。
目录
第0章 概述
第1章 jmock初体验
第2章 期望
第3章 返回值
第4章 参数匹配
第5章 指定方法调用次数
第6章 指定执行序列
第7章 状态机
第0章 概述
现在的dev不是仅仅要写code而已,UT已经变为开发中不可缺少的一环。JUnit的出现给javaer的UT编写提供了巨大的便利。但是JUnit并没有解决所有的问题。
当我们要测试一个功能点的时候,需要把不需要我们关注的东西隔离开,从而可以只关注我们需要关注的行为。
jmock通过mock对象来模拟一个对象的行为,从而隔离开我们不关心的其他对象,使得UT的编写变得更为可行,也使得TDD变得更为方便,自然而然的,也就成为敏捷开发的一个利器。
可以到http://www.jmock.org/download.html下载jmock.
添加jar到classpath。
添加的时候,注意把JUnit4的order放到最后。因为junit4它自己带了一个Hamcrest jar。
要是不注意顺序的话,有可能报
java.lang.SecurityException: class "org.hamcrest.TypeSafeMatcher"'s signer information does not match signer information of other classes in the same package。
Note:
这里的类定义用来演示如何使用jmock,所以都是定义为public的。
Java代码
01.public class UserManager {
02.
03. public AddressService addressService;
04.
05. public Address findAddress(String userName) {
06. return addressService.findAddress(userName);
07. }
08.
09. public Iterator<Address> findAddresses(String userName) {
10. return addressService.findAddresses(userName);
11. }
12.}
我们有一个UserManager,要测试它的方法,但是,UserManager是依赖于AddressService的。这里我们准备mock掉AddressService。
第1章 jmock初体验
这个例子的作用在于像一个传统的hello world一样,给大家一个简明的介绍,可以有一个感觉,jmock可以做什么。
AddressService本身太复杂,很难构建,这个时候,jmock出场了。
Java代码
01.@Test
02.public void testFindAddress() {
03.
04. // 建立一个test上下文对象。
05. Mockery context = new Mockery();
06.
07. // 生成一个mock对象
08. final AddressService addressServcie = context
09. .mock(AddressService.class);
10.
11. // 设置期望。
12. context.checking(new Expectations() {
13. {
14. // 当参数为"allen"的时候,addressServcie对象的findAddress方法被调用一次,并且返回西安。
15. oneOf(addressServcie).findAddress("allen");
16. will(returnValue(Para.Xian));
17. }
18. });
19.
20. UserManager manager = new UserManager();
21.
22. // 设置mock对象
23. manager.addressService = addressServcie;
24.
25. // 调用方法
26. Address result = manager.findAddress("allen");
27.
28. // 验证结果
29. Assert.assertEquals(Result.Xian, result);
30.
31.}
那么这里做了什么事情呢?
1 首先,我们建立一个test上下文对象。
2 用这个mockery context建立了一个mock对象来mock AddressService.
3 设置了这个mock AddressService的findAddress应该被调用1次,并且参数为"allen"。
4 生成UserManager对象,设置addressService,调用findAddress。
5 验证期望被满足。
基本上,一个简单的jmock应用大致就是这样一个流程。
最显著的优点就是,我们没有AddressService的具体实现,一样可以测试对AddressService接口有依赖的其他类的行为。也就是说,我们通过mock一个对象来隔离这个对象对要测试的代码的影响。
由于大致的流程是一样的,我们提供一个抽象类来模板化jmock的使用。
Java代码
01.public abstract class TestBase {
02.
03. // 建立一个test上下文对象。
04. protected Mockery context = new Mockery();
05.
06. // 生成一个mock对象
07. protected final AddressService addressServcie = context
08. .mock(AddressService.class);
09.
10. /**
11. * 要测试的userManager.
12. * */
13. protected UserManager manager;
14.
15. /**
16. * 设置UserManager,并且设置mock的addressService。
17. * */
18. private void setUpUserManagerWithMockAddressService() {
19. manager = new UserManager();
20. // 设置mock对象
21. manager.addressService = addressServcie;
22. }
23.
24. /**
25. * 调用findAddress,并且验证返回值。
26. *
27. * @param userName
28. * userName
29. * @param expected
30. * 期望返回的地址。
31. * */
32. protected void assertFindAddress(String userName, Address expected) {
33. Address address = manager.findAddress(userName);
34. Assert.assertEquals(expected, address);
35. }
36.
37. /**
38. * 调用findAddress,并且验证方法抛出异常。
39. * */
40. protected void assertFindAddressFail(String userName) {
41. try {
42. manager.findAddress(userName);
43. Assert.fail();
44. } catch (Throwable t) {
45. // Nothing to do.
46. }
47. }
48.
49. @Test
50. public final void test() {
51.
52. setUpExpectatioin();
53.
54. setUpUserManagerWithMockAddressService();
55.
56. invokeAndVerify();
57. }
58.
59. /**
60. * 建立期望。
61. * */
62. protected abstract void setUpExpectatioin();
63.
64. /**
65. * 调用方法并且验证结果。
66. * */
67. protected abstract void invokeAndVerify();
68.}
这样一来,我们以后的例子中只用关心setUpExpectatioin()和invokeAndVerify()方法就好了。
第2章 期望
好了,让我们来看看一个期望的框架。
Java代码
01.invocation-count (mock-object).method(argument-constraints);
02. inSequence(sequence-name);
03. when(state-machine.is(state-name));
04. will(action);
05. then(state-machine.is(new-state-name));
invocation-count 调用的次数约束
mock-object mock对象
method 方法
argument-constraints 参数约束
inSequence 顺序
when 当mockery的状态为指定的时候触发。
will(action) 方法触发的动作
then 方法触发后设置mockery的状态
这个稍微复杂一些,一下子不明白是正常的,后面讲到其中的细节时,可以回来在看看这个框架。
第3章 返回值
调用一个方法,可以设置它的返回值。即设置will(action)。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. context.checking(new Expectations() {
04. {
05. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
06. allowing(addressServcie).findAddress("allen");
07. will(returnValue(Para.BeiJing));
08.
09. // 当参数为null的时候,抛出IllegalArgumentException异常。
10. allowing(addressServcie).findAddress(null);
11. will(throwException(new IllegalArgumentException()));
12. }
13. });
14.}
15.
16.@Override
17.protected void invokeAndVerify() {
18. assertFindAddress("allen", Result.BeiJing);
19. assertFindAddressFail(null);
20.}
这里演示了两种调用方法的结果,返回值和抛异常。
使用jmock可以返回常量值,也可以根据变量生成返回值。
抛异常是同样的,可以模拟在不同场景下抛的各种异常。
对于Iterator的返回值,jmock也提供了特殊支持。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 生成地址列表
04. final List<Address> addresses = new ArrayList<Address>();
05. addresses.add(Para.Xian);
06. addresses.add(Para.HangZhou);
07.
08. final Iterator<Address> iterator = addresses.iterator();
09.
10. // 设置期望。
11. context.checking(new Expectations() {
12. {
13. // 当参数为"allen"的时候,addressServcie对象的findAddresses方法用returnvalue返回一个Iterator<Address>对象。
14. allowing(addressServcie).findAddresses("allen");
15. will(returnValue(iterator));
16.
17. // 当参数为"dandan"的时候,addressServcie对象的findAddresses方法用returnIterator返回一个Iterator<Address>对象。
18. allowing(addressServcie).findAddresses("dandan");
19. will(returnIterator(addresses));
20. }
21. });
22.
23.}
24.
25.@Override
26.protected void invokeAndVerify() {
27.
28. Iterator<Address> resultIterator = null;
29.
30. // 第1次以"allen"调用方法
31. resultIterator = manager.findAddresses("allen");
32. // 断言返回的对象。
33. assertIterator(resultIterator);
34.
35. // 第2次以"allen"调用方法,返回的与第一次一样的iterator结果对象,所以这里没有next了。
36. resultIterator = manager.findAddresses("allen");
37. Assert.assertFalse(resultIterator.hasNext());
38.
39. // 第1次以"dandan"调用方法
40. resultIterator = manager.findAddresses("dandan");
41. // 断言返回的对象。
42. assertIterator(resultIterator);
43.
44. // 第2次以"dandan"调用方法,返回的是一个全新的iterator。
45. resultIterator = manager.findAddresses("dandan");
46. // 断言返回的对象。
47. assertIterator(resultIterator);
48.}
49.
50./** 断言resultIterator中有两个期望的Address */
51.private void assertIterator(Iterator<Address> resultIterator) {
52. Address address = null;
53. // 断言返回的对象。
54. address = resultIterator.next();
55. Assert.assertEquals(Result.Xian, address);
56. address = resultIterator.next();
57. Assert.assertEquals(Result.HangZhou, address);
58. // 没有Address了。
59. Assert.assertFalse(resultIterator.hasNext());
60.}
从这个例子可以看到对于Iterator,returnValue和returnIterator的不同。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
07. allowing(addressServcie).findAddress("allen");
08. will(new Action() {
09.
10. @Override
11. public Object invoke(Invocation invocation)
12. throws Throwable {
13. return Para.Xian;
14. }
15.
16. @Override
17. public void describeTo(Description description) {
18. }
19. });
20. }
21. });
22.}
23.
24.@Override
25.protected void invokeAndVerify() {
26. assertFindAddress("allen", Result.Xian);
27.}
其实这里要返回一个Action,该Action负责返回调用的返回值。既然知道了这个道理,我们自然可以自定义Action来返回方法调用的结果。
而returnValue,returnIterator,throwException只不过是一些Expectations提供的一些static方法用来方便的构建不同的Action。
除了刚才介绍的
ReturnValueAction 直接返回结果
ThrowAction 抛出异常
ReturnIteratorAction 返回Iterator
还有
VoidAction
ReturnEnumerationAction 返回Enumeration
DoAllAction 所有的Action都执行,但是只返回最后一个Action的结果。
ActionSequence 每次调用返回其Actions列表中的下一个Action的结果。
CustomAction 一个抽象的Action,方便自定义Action。
举个例子来说明DoAllAction和ActionSequence的使用。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // doAllAction
07. allowing(addressServcie).findAddress("allen");
08. will(doAll(returnValue(Para.Xian), returnValue(Para.HangZhou)));
09.
10. // ActionSequence
11. allowing(addressServcie).findAddress("dandan");
12. will(onConsecutiveCalls(returnValue(Para.Xian),
13. returnValue(Para.HangZhou)));
14. }
15. });
16.}
17.
18.@Override
19.protected void invokeAndVerify() {
20. assertFindAddress("allen", Result.HangZhou);
21.
22. assertFindAddress("dandan", Result.Xian);
23. assertFindAddress("dandan", Result.HangZhou);
24.
25.}
第4章 参数匹配
即设置argument-constraints
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03. // 设置期望。
04. context.checking(new Expectations() {
05. {
06. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
07. allowing(addressServcie).findAddress("allen");
08. will(returnValue(Para.Xian));
09.
10. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
11. allowing(addressServcie).findAddress(with(equal("dandan")));
12. will(returnValue(Para.HangZhou));
13.
14. // 当参数包含"zhi"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. allowing(addressServcie).findAddress(
16. with(new BaseMatcher<String>() {
17.
18. @Override
19. public boolean matches(Object item) {
20. String value = (String) item;
21. if (value == null)
22. return false;
23. return value.contains("zhi");
24. }
25.
26. @Override
27. public void describeTo(Description description) {
28. }
29.
30. }));
31.
32. will(returnValue(Para.BeiJing));
33.
34. // 当参数为其他任何值的时候,addressServcie对象的findAddress方法返回一个Adress对象。
35. allowing(addressServcie).findAddress(with(any(String.class)));
36.
37. will(returnValue(Para.ShangHai));
38. }
39. });
40.
41.}
42.
43.@Override
44.protected void invokeAndVerify() {
45. // 以"allen"调用方法
46. assertFindAddress("allen", Result.Xian);
47. // 以"dandan"调用方法
48. assertFindAddress("dandan", Result.HangZhou);
49. // 以包含"zhi"的参数调用方法
50. assertFindAddress("abczhidef", Result.BeiJing);
51. // 以任意一个字符串"abcdefg"调用方法
52. assertFindAddress("abcdefg", Result.ShangHai);
53.}
测试演示了直接匹配,equal匹配,自定义匹配,任意匹配。
其实,这些都是为了给参数指定一个Matcher,来决定调用方法的时候,是否接收这个参数。
在Expectations中提供了一些便利的方法方便我们构造Matcher.
其中
equal判断用equal方法判断是否相等。
same判断是否是同一个引用。
any,anything接收任意值。
aNull接收null。
aNonNull接收非null.
jmock提供了很多有用的匹配。可以用来扩展写出更多的Matcher。
基本Matcher
IsSame 引用相等。
IsNull
IsInstanceOf
IsEqual 考虑了数组的相等(长度相等,内容equals)
IsAnything always return true.
逻辑Matcher
IsNot
AnyOf
AllOf
其他
Is 装饰器模式的Matcher,使得可读性更高。
第5章 指定方法调用次数
可以指定方法调用的次数。即对invocation-count进行指定。
exactly 精确多少次
oneOf 精确1次
atLeast 至少多少次
between 一个范围
atMost 至多多少次
allowing 任意次
ignoring 忽略
never 从不执行
可以看出,这些range都是很明了的。只有allowing和ignoring比较特殊,这两个的实际效果是一样的,但是关注点不一样。当我们允许方法可以任意次调用时,用allowing,当我们不关心一个方法的调用时,用ignoring。
第6章 指定执行序列
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final Sequence sequence = context.sequence("mySeq_01");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
10. oneOf(addressServcie).findAddress("allen");
11. inSequence(sequence);
12. will(returnValue(Para.Xian));
13.
14. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. oneOf(addressServcie).findAddress("dandan");
16. inSequence(sequence);
17. will(returnValue(Para.HangZhou));
18.
19. }
20. });
21.
22.}
23.
24.@Override
25.protected void invokeAndVerify() {
26. assertFindAddress("allen", Result.Xian);
27. assertFindAddress("dandan", Result.HangZhou);
28.}
这里指定了调用的序列。使得调用必须以指定的顺序调用。
来看一个反例
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final Sequence sequence = context.sequence("mySeq_01");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 当参数为"allen"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
10. oneOf(addressServcie).findAddress("allen");
11. inSequence(sequence);
12. will(returnValue(Para.Xian));
13.
14. // 当参数为"dandan"的时候,addressServcie对象的findAddress方法返回一个Adress对象。
15. oneOf(addressServcie).findAddress("dandan");
16. inSequence(sequence);
17. will(returnValue(Para.HangZhou));
18.
19. }
20. });
21.}
22.
23.@Override
24.protected void invokeAndVerify() {
25. assertFindAddressFail("dandan");
26.}
当指定序列的第一个调用没有触发的时候,直接调用第2个,则会抛异常。
Note:指定序列的时候注意方法调用次数这个约束,如果是allowing那么在这个序列中,它是可以被忽略的。
第7章 状态机
状态机的作用在于模拟对象在什么状态下调用才用触发。
Java代码
01.@Override
02.protected void setUpExpectatioin() {
03.
04. final States states = context.states("sm").startsAs("s1");
05.
06. // 设置期望。
07. context.checking(new Expectations() {
08. {
09. // 状态为s1参数包含allen的时候返回西安
10. allowing(addressServcie).findAddress(
11. with(StringContains.containsString("allen")));
12. when(states.is("s1"));
13. will(returnValue(Para.Xian));
14.
15. // 状态为s1参数包含dandan的时候返回杭州,跳转到s2。
16. allowing(addressServcie).findAddress(
17. with(StringContains.containsString("dandan")));
18. when(states.is("s1"));
19. will(returnValue(Para.HangZhou));
20. then(states.is("s2"));
21.
22. // 状态为s2参数包含allen的时候返回上海
23. allowing(addressServcie).findAddress(
24. with(StringContains.containsString("allen")));
25. when(states.is("s2"));
26. will(returnValue(Para.ShangHai));
27. }
28. });
29.}
30.
31.@Override
32.protected void invokeAndVerify() {
33. // s1状态
34. assertFindAddress("allen", Result.Xian);
35. assertFindAddress("allen0", Result.Xian);
36.
37. // 状态跳转到 s2
38. assertFindAddress("dandan", Result.HangZhou);
39.
40. // s2状态
41. assertFindAddress("allen", Result.ShangHai);
42.}
可以看到,如果序列一样,状态也为期望的执行设置了约束,这里就是用状态来约束哪个期望应该被执行。
可以用is或者isNot来限制状态。
状态机有一个很好的用处。
当我们建立一个test执行上下文的时候,如果建立的时候和执行的时候,我们都需要调用mock ojbect的方法,那么我们可以用状态机把这两部分隔离开。让他们在不同的状态下执行。
相关推荐
除了基本的模拟和期待,jMock还支持更复杂的场景,如顺序(顺序调用模拟对象的方法)、条件(某些方法的调用依赖于其他方法的调用结果)和回调(模拟对象方法的返回值取决于其他方法的调用)等。这些特性使得jMock...
【jmock-2.6.0-jars】这个压缩包主要包含了`jmock`库的2.6.0版本的jar文件以及相关的源代码。`jmock`是Java平台上的一个模拟框架,它允许开发者在单元测试中创建和控制对象的行为,以便能够隔离测试并精确地指定期望...
jmock-cglib-1.2.0.jar jmock-cglib-1.2.0.jar jmock-cglib-1.2.0.jar
jMock利用mock objects思想来对Java code进行测试。jMock具有以下特点:容易扩展,让你快速简单地定义mock objects,因此不必打破程序间的关联,让你定义灵活的超越对象之间交互作用而带来测试局限,减少你测试地脆弱...
这个压缩包中的两个主要文件——jmock-core-1.2.0.jar和jmock-cglib-1.2.0.jar,是进行JMock测试时所必需的组件。 首先,我们来看jmock-core-1.2.0.jar。这是JMock的核心库,提供了模拟对象的基本功能。它包括了...
首先,理解jMock的基本理念至关重要。jMock基于Mock Objects设计模式,这种模式在软件开发中用于隔离被测试代码与它的依赖关系。通过创建模拟对象,我们可以在测试时控制依赖的行为,从而更准确地测试代码逻辑。...
在 JUnit 中,你可以使用 `jmock-junit3` 或 `jmock-junit4` 包,它们提供了一个 JUnit Rule,使得在测试类中使用 JMock 更加方便。 6. **版本信息** 这个压缩包中的 "jmock-1.2.0-jars" 提供的是 JMock 1.2.0 ...
Spock是针对Java和Groovy应用程序的测试和规范框架。 使它在人群中脱颖而出的是其美丽而富有表现力的规范语言。 由于其JUnit运行程序,...org.spockframework/spock-core/1.3-groovy-2.5/spock-core-1.3-groovy-2.5.jar
jMock-2.4.0-jars.zip是一个包含jMock库的压缩文件,它在软件开发领域,特别是单元测试方面扮演着至关重要的角色。jMock是一个开源的Java模拟框架,其目标是简化单元测试,帮助开发者创建更加健壮、高覆盖率的测试...
jMock 2: Java 5 and above. Stable: 2.5.1 Binary JARs (includes the source to let IDEs provide context-sensitive help) Javadocs Unstable: 2.6.0-RC2 Binary JARs (includes the source to let IDEs ...
Maven-Central / jmock / jmock-cglib 1.2.0 Maven-Central / org.apache.activemq / activemq-ra 5.2.05.1.0 Maven-Central / org.apache.maven.shared / maven-dependency-tree 1.21.1 Maven-Central / org.apache...
jmock-1.0.1.jar jmock-1.0.1.jar
1. **添加依赖**:将`jmock-2.6.0.jar`添加到项目的类路径中,通常通过Maven或Gradle等构建工具进行管理。 2. **创建模拟对象**:使用`Mockery`类创建一个上下文,然后在这个上下文中定义你要模拟的对象。 3. **...
jmock-2.5.1.jar jmock-2.5.1.jar jmock-2.5.1.jar
Spock是针对Java和Groovy应用程序的测试和规范框架。 使它在人群中脱颖而出的是其美丽而富有表现力的规范语言。...org.spockframework/spock-core/1.3-RC1-groovy-2.5/spock-core-1.3-RC1-groovy-2.5.jar
不管是测试驱动开发或者是其它的开发模式,都会需要对代码进行单元测试,对于关联依赖关系少或者比较简单的类来说,直接使用JUnit就可以轻松的完成测试工作,但是对于关联到...在这里使用JMock来模拟那些不需要测试的类
《JMOCK-2.6.0:强化Java单元测试的艺术》 在软件开发的世界里,测试是确保代码质量不可或缺的一环。而JMOCK,作为一款强大的Java模拟框架,正是为了解决这一问题应运而生。"JMOCK-2.6.0"是一个专门用于项目测试的...
`jmock-2.6.0-RC2-javadoc.zip`和`jmock-2.5.1-javadoc.zip`包含的是`jmock`的API文档,可以帮助开发者理解和使用其API。`jmock-2.6.0-RC2-jars.zip`和`jmock-2.5.1-jars.zip`则包含了相应的库文件,可以直接在项目...