Moq & RhinoMocks

使用Mock对象进行测试一般都会有以下三个关键步骤:

  1. 使用接口来描述需要测试的对象
  2. 为实际的产品代码实现这个接口
  3. 以测试为目的,在Mock对象中实现这个接口

在使用Mock对象的过程中,充分体现出了“面向接口编程”的设计原则,同时也促成类的良好设计。

自行实现Mock对象是相当繁琐的工作,让人幸运的是,在.NET世界中有多个优秀的Mock框架可以供大家选择,目前最常使用的无非MoqRhino Mocks这两个框架。两者的最新版本在Mocking API方面的用法已日趋一致,都依托Lambda表达式、泛型和扩展方法做了很大改进,目标都是让Mock对象以一种更自然的方式与多个单元测试框架进行集成,以一种清晰的语法来描述期望值、参数约束、返回值等,极大的方便开发者的使用。

由于Moq和Rhino Mocks都使用了Castle DynamicProxy这个类库动态生成代理类,因此对需要Mock的对象有一定的限制:所测试的方法必须是virtual类型。

下面就用一个例子来看看两者的不同实现(这个例子摘自Moq源代码包中的Samples,只是略做了些修改以便于展现两者的特点):

mocks 

需要进行测试的对象如下示之:

展开
折叠

public class OrdersPresenter
{
    private IRepository<Order> repository { get; set; }
    public IOrdersView View { get; set; }

    public OrdersPresenter(IOrdersView view, IRepository<Order> repository) {
        this.View = view;
        this.repository = repository;
        this.View.OrderSelected += (sender, args) => DoOrderSelection(args.Order);
    }

    public Order SelectedOrder { get; private set; }

    private void DoOrderSelection(Order selectedOrder) {
        // Do something when the view selects an order.
        SelectedOrder = selectedOrder;
    }

    public virtual void OnInit() {
        View.Orders = repository.FindAll();
    }           
}

下面是用这两个Mock框架分别实现的单元测试代码:

Moq 4.0Rhino 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都非常接近,使用两者任何一个都能方便实现你的测试目的。

References

  1. QuickStart - Moq
  2. Ayende@Wiki - Rhino Mocks 3.5
  3. Daniel Cazzulino's Blog - Why do we need yet another NET mocking framework

转载于:https://www.cnblogs.com/huyh/archive/2010/06/14/1758143.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值