单元测试—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

相关文章

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

数据治理框架-ISO数据治理标准

引言 "数据治理"并不是一个新的概念,国内外有很多组织专注于数据治理理论和实践的研究。目前国际上,主要的数据治理框架有ISO数据治理标准、GDI数据治理框架、DAMA数据治理管理框架等。 ISO数据治理标准 改标准阐述了数据治理的标准、基本原则和数据治理模型,是一套完整的数据治理方法论。 ISO/IEC 38505标准的数据治理方法论的核心内容如下: 数据治理的目标:促进组织高效、合理地

ZooKeeper 中的 Curator 框架解析

Apache ZooKeeper 是一个为分布式应用提供一致性服务的软件。它提供了诸如配置管理、分布式同步、组服务等功能。在使用 ZooKeeper 时,Curator 是一个非常流行的客户端库,它简化了 ZooKeeper 的使用,提供了高级的抽象和丰富的工具。本文将详细介绍 Curator 框架,包括它的设计哲学、核心组件以及如何使用 Curator 来简化 ZooKeeper 的操作。 1

【Kubernetes】K8s 的安全框架和用户认证

K8s 的安全框架和用户认证 1.Kubernetes 的安全框架1.1 认证:Authentication1.2 鉴权:Authorization1.3 准入控制:Admission Control 2.Kubernetes 的用户认证2.1 Kubernetes 的用户认证方式2.2 配置 Kubernetes 集群使用密码认证 Kubernetes 作为一个分布式的虚拟

Spring Framework系统框架

序号表示的是学习顺序 IoC(控制反转)/DI(依赖注入): ioc:思想上是控制反转,spring提供了一个容器,称为IOC容器,用它来充当IOC思想中的外部。 我的理解就是spring把这些对象集中管理,放在容器中,这个容器就叫Ioc这些对象统称为Bean 用对象的时候不用new,直接外部提供(bean) 当外部的对象有关系的时候,IOC给它俩绑好(DI) DI和IO

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

利用Django框架快速构建Web应用:从零到上线

随着互联网的发展,Web应用的需求日益增长,而Django作为一个高级的Python Web框架,以其强大的功能和灵活的架构,成为了众多开发者的选择。本文将指导你如何从零开始使用Django框架构建一个简单的Web应用,并将其部署到线上,让世界看到你的作品。 Django简介 Django是由Adrian Holovaty和Simon Willison于2005年开发的一个开源框架,旨在简