.Net中单元测试工具的比较(Moq, VS Fake, TypeMock)
单元测试是项目开发中的重要一环, 良好的单元测试不仅会让程序员编写出良好的代码,也同时能提高程序的质量。如果要进行充分的单元测试,那么就需要借助一些工具帮助我们写出更好的测试用例。
本文的目的是对一些现有的测试工具进行比较,主要是从测试用例的创建与测试工具的功能方面区别通过代码的方式做出比较。
本文将这些工具工具分为三类:
- 第一类是一些免费开源的测试工具,比如Moq, Rhino Mocks等,我称它们为Mock Framework.
- 第二类是VS 2012中的Fake Framework
- 第三类则是市面上的商业软件,比如TypeMock, JustMock.
文中将以Moq源码中的sample来对这些工具进行比较:
下面是部分代码的关系图:
1. Mock Framework
以Moq为例,它主要是通过动态生成代理类来实现测试用例的编写,因此使用它的时候有一个限制,那就是测试的方法都要是public virtual类型。使用Moq时只要引用它的Assembly,非常方便。
我们看看下面这个测试用例,它有以下几个部分:
- 通过泛型产生代理实例,设置实例中方法的行为。
- 执行要测试的方法
- 验证测试结果,包括一些值和方法的调用。
[TestMethod] // Mock return value public void ShouldPlaceOrderIfEnoughInventory() { // Arrange var catalog = new Mock<ICatalogService>(); var view = new Mock<IProductsView>(); var presenter = new ProductsPresenter(catalog.Object, view.Object); var order = new Order { Product = new Product { Id = 1 }, Quantity = 5 }; catalog.Setup(c => c.HasInventory(1, 5)).Returns(true); // Act presenter.PlaceOrder(order); // Assert Assert.IsTrue(order.Filled); catalog.Verify(c => c.HasInventory(1, 5)); }
2. VS Fake
微软在VS 2012 Ultimate加入了一个Fake Framework来更好的支持测试用例,Fake Framework包括两个特性 – Stub和Shim。 如果我们需要对一个Assembly进行测试,只要在References里的Assembly上点击右键,然后选择Add fakes assembly。VS会自动生成一个新的Fake Assembly,它对Assembly里的interface和含virtual方法的类生成一个Stub类,对非interface类生成一个Shim类。
Stub: 首先我们看一个同样的ShouldPlaceOrderIfEnoughInventory测试用例,可以看到VS直接生成一个代理类,而它的方法的行为是通过委托来完成的。但是Fake Framework中不能进行方法的Verify.
[TestMethod] public void ShouldPlaceOrderIfEnoughInventory() { // Arrange var catalog = new StubCatalogService(); var view = new StubIProductsView(); var presenter = new ProductsPresenter(catalog, view); view.CategorySelectedEvent.Invoke(view, new CategoryEventArgs(new Category())); var order = new Order { Product = new Product { Id = 1 }, Quantity = 5 }; catalog.HasInventoryInt32Int32 = (a, b) => true; // Act presenter.PlaceOrder(order); // Assert Assert.IsTrue(order.Filled); }
Shim: Shim是针对非virtual方法的测试,它可以看成一个Runtime的interceptor. 假设我们有一个方法返回值是通过DateTime.Now计算出的,但是DateTime.Now是时刻在变化的,这时候就可以通过Shim来写测试用例.
[TestMethod] public void ShimTest() { using (ShimsContext.Create()) { ShimDateTime.NowGet = () => new DateTime(2000, 1, 1); Assert.AreEqual(DateTime.Now.Year, 2000); } }
3. TypeMock等商用软件
TypeMock, JustMock都是商用测试工具,因此功能上比前面的要多一些。除此之外TypeMock还支持Mock私有方法等。在安装完软件后,引用相关的dll就可以编写测试用例了。
[TestMethod] public void ShouldPlaceOrderIfEnoughInventoryByTypeMock() { // Arrange var catalog = Isolate.Fake.Instance<CatalogService>(); var view = Isolate.Fake.Instance<IProductsView>(); var presenter = new ProductsPresenter(catalog, view); var order = new Order { Product = new Product { Id = 1 }, Quantity = 5 }; Isolate.WhenCalled(() => catalog.HasInventory(1, 5)).WithExactArguments().WillReturn(true); // Act presenter.PlaceOrder(order); // Assert Assert.IsTrue(order.Filled); Isolate.Verify.WasCalledWithExactArguments(() => catalog.HasInventory(1, 5)); }
[TestMethod] public void DateTimeNowTestByTypeMock() { Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1)); Assert.AreEqual(DateTime.Now.Year, 2000); }
结语:
在我们选用测试用例的时候需要根据我们的实际情况选择测试工具,比如是否只对interface和public方法进行测试,正版软件的License费用,工具的持续性维护等等。