Moq & RhinoMocks
使用Mock对象进行测试一般都会有以下三个关键步骤:
- 使用接口来描述需要测试的对象
- 为实际的产品代码实现这个接口
- 以测试为目的,在Mock对象中实现这个接口
在使用Mock对象的过程中,充分体现出了“面向接口编程”的设计原则,同时也促成类的良好设计。
自行实现Mock对象是相当繁琐的工作,让人幸运的是,在.NET世界中有多个优秀的Mock框架可以供大家选择,目前最常使用的无非Moq与Rhino Mocks这两个框架。两者的最新版本在Mocking API方面的用法已日趋一致,都依托Lambda表达式、泛型和扩展方法做了很大改进,目标都是让Mock对象以一种更自然的方式与多个单元测试框架进行集成,以一种清晰的语法来描述期望值、参数约束、返回值等,极大的方便开发者的使用。
由于Moq和Rhino Mocks都使用了Castle DynamicProxy这个类库动态生成代理类,因此对需要Mock的对象有一定的限制:所测试的方法必须是virtual类型。
下面就用一个例子来看看两者的不同实现(这个例子摘自Moq源代码包中的Samples,只是略做了些修改以便于展现两者的特点):
需要进行测试的对象如下示之:
展开
下面是用这两个Mock框架分别实现的单元测试代码:
Moq 4.0 | Rhino Mocks 3.6 |
[Test] public void TestPresenterSelection() { // arrange var mView = new Mock<IOrdersView>(); var mRepository = new Mock<IRepository<Order>>(); var presenter = new OrdersPresenter(mView.Object, mRepository.Object); // check that the presenter has no selection by default Assert.Null(presenter.SelectedOrder); // raise event mView.Raise(io => io.OrderSelected += null, new OrderEventArgs { Order = new Order("moq", 50) }); // assert Assert.NotNull(presenter.SelectedOrder); Assert.AreEqual("moq", presenter.SelectedOrder.ProductName); } |
[Test] public void TestPresenterSelection() { // arrange var mView = MockRepository.GenerateMock<IOrdersView>(); var mRepository = MockRepository.GenerateMock<IRepository<Order>>(); var presenter = new OrdersPresenter(mView, mRepository); // check that the presenter has no selection by default Assert.Null(presenter.SelectedOrder); // raise event mView.Raise(io => io.OrderSelected += null, null, new OrderEventArgs { Order = new Order("moq", 50) }); // assert Assert.NotNull(presenter.SelectedOrder); Assert.AreEqual("moq", presenter.SelectedOrder.ProductName); } |
[Test] public void TestRetrieveOrders() { // arrange var mView = new Mock<IOrdersView>(); var mRepository = new Mock<IRepository<Order>>(); var presenter = new OrdersPresenter(mView.Object, mRepository.Object); List<Order> defaultOrders = new List<Order> { new Order("moq"), new Order("RhinoMock") }; mRepository.Setup(r => r.FindAll()).Returns(defaultOrders); // exercise mocks presenter.OnInit(); // assert mView.VerifySet(v => v.Orders = defaultOrders); } |
[Test] public void TestRetrieveOrders() { // arrange var mView = MockRepository.GenerateMock<IOrdersView>(); var mRepository = MockRepository.GenerateStub<IRepository<Order>>(); var presenter = new OrdersPresenter(mView, mRepository); List<Order> defaultOrders = new List<Order> { new Order("moq"), new Order("RhinoMock") }; mRepository.Stub(ir => ir.FindAll()).Return(defaultOrders); // exercise mocks presenter.OnInit(); // assert mView.AssertWasCalled(v => v.Orders = defaultOrders); } |
Conclusion
通过上面的实例我们可以很容易看出两者的Syntax与API都非常接近,使用两者任何一个都能方便实现你的测试目的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
2014-05-05 GridView动态增加行
2013-05-05 ArcEngine IMap接口
2013-05-05 Database2Sharp之混合型Winform框架代码生成
2013-05-05 vim配置文件和插件
2013-05-05 .NET:在ASP.NET中如何进行IP限制
2013-05-05 初学Perl的感受之数据类型
2013-05-05 通过Linux理解操作系统(一):概述