单元测试—Moq框架

2024-05-08 02:32
文章标签 框架 单元测试 moq

本文主要是介绍单元测试—Moq框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【概念】

        

        Moq(英语发音是Mock-you 或者只是mock)是一个针对.Net开发的模拟库,它从开始就完全充分利用了.NET3.5(LINQ表达式树)和C#3.0的新特性(lambda表达式)。它的目标是让模拟以一种自然的方式与现有单元测试进行集成,使它更加简单、直观,以避免开发人员被迫重写测试或高成本的学习测试框架。这使它成为了一个高生产力、类型安全、重构友好的模拟库。

关于Moq的基本介绍,大家可以移步《.NET平台测试驱动开发模拟框架Moq简明教程(简介)》


一、可以使用Moq模拟哪些内容?


          你可以针对接口和现有类来使用Moq创建模拟对象。当应用于类时,需要具备一定的条件:类不能是封闭类型的(sealed);而且,被模拟的方法必须标记为虚拟类型(virtual)的。你无法简单地模拟静态方法(但是你可以使用Adaptor模式来模拟一个静态方法)。其实,上面这些限制条件与你使用另一个模拟对象框架Rhino Mocks时是一致的。

          Moq和Rhino Mocks在后台实现上都使用了代理类的技术。而且,更深入一步了解,这两个框架都派生自相同的Castle DynamicProxy代码基类。


二、使用Moq对方法和属性进行模拟


        现在,不妨设想一下,你正在编写一个数剧库驱动的Web应用程序-例如一个在线商店。在进行任何其他部分的编码之前,你想首先完成这个电子商店软件的业务逻辑部分的设计。特别是,在编写完你的业务逻辑组件之前,你根本不想投入任何精力去编写数据访问有关的组件。

     上面这种情形下特别适合于使用一种模拟对象框架。此时,你可以创建一个接口,用于描述你想使你的数据访问组件看上去的样子。然后,你便可以简单地模拟此接口,并且在测试你的业务逻辑时充分地利用模拟对象的优势(即不需要真正地实现被模拟的组件)。因此,借助于这种模拟方案,你可以先不考虑这些组件有关的编码,直到你已经为这部分被模拟组件的编程作好了充分的准备。

     列表1中提供的第一个接口名字为IProductRepository,此描述共描述了两个方法。其中,第一个方法Select()负责返回数据库中所有的产品。第二个方法Get()根据给定的特定产品ID返回一个产品。此外,列表1还提供了一个接口,名字为IProduct;此接口用于描述某一种特定的产品。

列表1–IProductRepository.cs

   using System;using System.Collections.Generic;namespace MoqSamples.Models{public interface IProductRepository{List<IProduct> Select();IProduct Get(int id);}public interface IProduct{int Id {get; set;}string Name { get; set; }}}
让我们首先来考虑模拟IProduct接口。下列代码将创建一个模拟的Product对象,此对象拥有一个Id属性且其属性值为1,还有一个Name属性且其属性值为“Bushmills”

//模拟一个Product对象var newProduct = new Mock<IProduct>();newProduct.ExpectGet(p => p.Id).Returns(1);newProduct.ExpectGet(p => p.Name).Returns("Bushmills");

上面的第一行代码从接口IProduct创建一个模拟。注意:这里所使用的Mock类是Moq框架所提供的,此类有一个泛型构造器,它能够接受李创建的接口类型。

接下来,建立了Id属性并且使之返回值1,又建立了Name属性并且使之返回值“Bushmills”。注意,此处是如何使用lambda表达式来描述Id和Name两个属性的。使用lambda表达式而不是使用字符串来描述一个属性的好处在于,支持例如像Resharper这样的重构工具能够对属性进行自动重构。

在上面创建完模拟对象newProduct后,接下来,你就可以像真正实现了接口IProduct一样来使用此模拟对象。例如,下面的断言(Assert)将会成功执行:

Assert.AreEqual("Bushmills", newProduct.Object.Name);

【注意】在此,当你引用模拟对象newProduct时必须使用newProduct.Object。这是因为newProduct变量代表的是代理类,而newProduct.Object变量代表的是实际的newProduct类。

接下来,让我们继续模拟IProductRepository接口。下列代码行创建了一个模拟对象IProductRepository,当调用它的Get()方法时它能够返回newProduct:

 //模拟ProductRepository接口var productRepository = new Mock<IProductRepository>();productRepository.Expect(p => p.Get(1)).Returns(newProduct.Object);
上面的第一行代码通过把接口IProductRepository传递给类Mock的泛型构造器创建相应的模拟对象。接下来,第二行代码建立Get()方法以返回模拟对象newProduct。请再次注意,这里也使用了lambda表达式来描述方法Get()。当创建模拟对象IProductRepository完毕,你就可以在你的测试代码中像下面这样来使用它了:

  // Actvar productReturned = productRepository.Object.Get(1);// AssertAssert.AreEqual("Bushmills", productReturned.Name);
在上面代码中,当你使用值1调用方法Get()时,返回的是newProduct对象。如果你调用除了1以外的其他值来调用Get()方法,那么将返回一个Null值。如果你想使得Get()方法返回newProduct,而不管传递给它是什么参数,那么你可以通过使用下列代码来创建模拟对象的方式实现:

  //模拟ProductRepository接口var productRepository = new Mock<IProductRepository>();productRepository.Expect(p => p.Get(It.IsAny<int>())).Returns(newProduct.Object);

请注意,这里当建立方法的期望时我们把约束It.IsAny<int>()传递给方法Get()。这个参数可以使模拟的Get()方法访问任何整数值(integer)并返回newProduct对象。

另外,借助于lambda表达式的帮助,你甚至可以指定你所能够想到的任何定制约束。请参考如下代码片断:

//模拟ProductRepository接口var productRepository = new Mock<IProductRepository>();productRepository.Expect(p => p.Get(It.Is<int>(id => id>0 && id<6))).Returns(newProduct.Object);
在上面的代码中,仅当传递给Get()方法的参数介于值06之间时才返回一个newProduct对象。显然,这里的约束条件是通过把一个lambda表达式传递给方法It.Is()实现的。


三、使用Moq的行为校验支持


Moq框架可用于执行有限的行为校验功能。例如,你可以使用Moq来检测在调用结束另一个方法之后是否至少调用了某一特定的方法一次。

注意:Moq对于行为校验功能的支持也是有限的。不像其他的模拟对象框架,例如Rhino Mocks和Typemock Isolator,你不能够使用Moq来对象之间的测试复杂的交互。Moq并没有实现像Rhino Mocks和Typemock Isolator所提供的对于同一类型的代码记录(record)与回放(replay)功能的支持。

那么,你该在何时使用行为校验呢?为此,Martin Fowler提供了一个内存缓存的例子。他认为,一个缓存的关键特征在于你无法从其状态中判断当前缓存是否在使用中(在未真正实现这部分编码及投入使用之前)。这种情形特别适合于行为校验-即使针对简单情形下的硬编码方案的测试驱动开发者来说也是这个结论。

现在,不妨设想你已经实现了一个类ProductRepository,此类包含一个名字为GetProduct()的方法,此方法用于产品检索。于是,这个方法首先会试图从缓存中取得商品对象。如果失败,那么此方法将继续努力从数据库中检索相应的产品。列表2给出了这个类相应的代码。

列表2–ProductRepository

   using System;using System.Web;namespace MoqSamples.Models{public class ProductRepository : IProductRepository{private ProductCache _cache;public ProductRepository(ProductCache cache){_cache = cache;}public virtual IProduct GetProduct(int id){var product = _cache.Get(id);if (product == null){product = this.Get(id);_cache.Set(id, product);}return product;}public virtual IProduct Get(int id){throw new NotImplementedException();}public System.Collections.Generic.List<IProduct> Select(){throw new NotImplementedException();}}}

在上面的代码中,通过依赖性注入方式,ProductCache对象被传递给ProductRepository类。在此,ProductRepository的GetProduct()方法首先试图从缓存对象中取得一个Product对象。如果获取失败的话,它将调用Get()方法来从数据库中取得此Product对象。注意,在此我没有给出Get()方法的实现。其实,我们所关注的仅是如何模拟它,所以这就足够了。

此外,注意到ProductCache类是一个强类型包装类,它所包装的是ASP.NET中十分重要的System.Web.Caching.Cache对象。





这篇关于单元测试—Moq框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/969055

相关文章

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

Python的端到端测试框架SeleniumBase使用解读

《Python的端到端测试框架SeleniumBase使用解读》:本文主要介绍Python的端到端测试框架SeleniumBase使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录SeleniumBase详细介绍及用法指南什么是 SeleniumBase?SeleniumBase

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component

Python Dash框架在数据可视化仪表板中的应用与实践记录

《PythonDash框架在数据可视化仪表板中的应用与实践记录》Python的PlotlyDash库提供了一种简便且强大的方式来构建和展示互动式数据仪表板,本篇文章将深入探讨如何使用Dash设计一... 目录python Dash框架在数据可视化仪表板中的应用与实践1. 什么是Plotly Dash?1.1