本文将介绍一款在.Net平台下的Mock工具---Rhino Mocks 2,以及相关Mock工具的一些比较.在了解到Rhino Mocks 2之前我也接触了一些其他的Mock工具, 比如EasyMock,JMock,NMock, NMock2,不过最终还是选择了Rhino Mocks 2, 相信你在看完本文的介绍后会和我做出同样的选择。(注: 本文不是Mock工具的入门文章,如果你之前尚未接触了解有关Mock对象,请先去了解相关资料)
本文由于编写时间较早, 现在RhinoMock有了新的变化,请参考RhinoMock2 续一文.
27 public interface ISubscriber
28 {
29 int MultiplyTwo(int i);
30 void Receive(Object message);
31 }
32
33 public class Publisher
34 {
35 private List<ISubscriber> subscribers = new List<ISubscriber>();
36
37 public void Add(ISubscriber s)
38 {
39 subscribers.Add(s);
40 }
41
42 public void Publish(object msg)
43 {
44 subscribers.ForEach(delegate(ISubscriber s) { s.Receive(msg); });
45 }
46 public int Caculate(int i)
47 {
48 int result = 0;
49 subscribers.ForEach(delegate(ISubscriber s) { result += s.MultiplyTwo(i); });
50 return result;
51 }
52 }
以上是一个Observer模式的小例子, 为了更加全面的体现出Mock对象的特性, 我在ISubscriber加了一个有返回值的方法MultiplyTwo, 它所做的事情就是将参数乘2并返回.
现在我们将对Publisher进行测试, 然而Publisher中涉及到了另一个对象ISubscriber. 而我们暂时还没实现ISubscriber , 所以我们将利用Mock Object来完成我的测试.
下面是4种Mock框架下的不同测试代码:
EasyMock.Net
55 namespace EasyMockTest
56 {
57 [TestFixture]
58 public class PublisherTest
59 {
60 [Test]
61 public void OneSubscriberReceivesAMessage()
62 {
63 //setup
64 MockControl mockCtrl= MockControl.CreateControl(typeof(ISubscriber));
65 ISubscriber subMock = mockCtrl.GetMock() as ISubscriber;
66 Publisher publisher = new Publisher();
67 publisher.Add(subMock);
68 object message = new object();
69
70 //record
71 mockCtrl.Reset();
72 subMock.Receive(message);
73 subMock.MultiplyTwo(5);
74 mockCtrl.SetReturnValue(10);
75 mockCtrl.Replay();
76
77 //execute
78 publisher.Publish(message);
79 Assert.AreEqual(10, publisher.Caculate(5));
80
81 //verify
82 mockCtrl.Verify();
83 }
84 }
85 }
NMock
55 namespace NMockTest
56 {
57 [TestFixture]
58 public class PublisherTest
59 {
60 [Test]
61 public void OneSubscriberReceivesAMessage()
62 {
63 // set up
64 Mock mockSubscriber = new DynamicMock(typeof(ISubscriber));
65 Publisher publisher = new Publisher();
66 publisher.Add((ISubscriber)mockSubscriber.MockInstance);
67 object message = new Object();
68
69 // expectations
70 mockSubscriber.Expect("Receive", message); //commentted is still ok
71 mockSubscriber.ExpectAndReturn("MultiplyTwo", 10, 5);
72
73 // execute
74 publisher.Publish(message);
75 Assert.AreEqual(10, publisher.Caculate(5));
76
77 // verify
78 mockSubscriber.Verify();
79 }
80 }
81 }
NMock2
55 namespace NMock2Test
56 {
57 [TestFixture]
58 public class PublisherTest
59 {
60 [Test]
61 public void OneSubscriberReceivesAMessage()
62 {
63 using (Mockery mocks = new Mockery())
64 {
65 //setup
66 ISubscriber subMock = mocks.NewMock(typeof(ISubscriber)) as ISubscriber;
67 Publisher publisher = new Publisher();
68 publisher.Add(subMock);
69 object message = new object();
70
71 //expectations
72 Expect.Once.On(subMock).Method("Receive").With(message);
73 Expect.Once.On(subMock).Method("MultiplyTwo").With(5).Will(Return.Value(10));
74
75 //excute
76 publisher.Publish(message);
77 Assert.AreEqual(10, publisher.Caculate(5));
78 }// verify when mocks dispose
79 }
80 }
81 }
RhinoMocks2
55 namespace RhinoMocks2Test
56 {
57 [TestFixture]
58 public class PublisherTest
59 {
60 [Test]
61 public void OneSubscriberReceivesAMessage()
62 {
63 using (MockRepository mocks = new MockRepository())
64 {
65 //setup
66 ISubscriber subMock = mocks.CreateMock(typeof(ISubscriber)) as ISubscriber;
67 Publisher publisher = new Publisher();
68 publisher.Add(subMock);
69 object message = new object();
70
71 //record with expectation model
72 subMock.Receive(message);
73 Expect.On(subMock).Call(subMock.MultiplyTwo(5)).Return(10);
74
75 //end record
76 mocks.ReplayAll();
77
78 //excute
79 publisher.Publish(message);
80 Assert.AreEqual(10, publisher.Caculate(5));
81 }//verify when mocks dispose
82 }
83 }
84 }
大致看来NMock2和RhinoMocks2比较相像, 尤其在创建Mock对象的时候, 这点也是较之EasyMock和NMock比较合理的地方, 因为你只需一个MockRepository就可以创建出多个Mock Object, 并且可以直接获得该类型的对象, 不需要再用mock.GetMock().这样的方法来获得所需的Mock对象.并且它们都利用using block增加方便性.(在using block结束的时候会调用using对象的Dispose方法,此时将会自动调用mocks.VerifyAll(), 不需要象EasyMock和NMock那样显式调用Verify方法.)
而NMock2