1、 都要以一个方法 这样的去测试

2、 利用工具 Install-Package Moq -Version 4.0 (最高的是4.5  4.0适用于自己项目的版本)   Moq.dll 进行测试  模拟接口类 跟抽象类

https://github.com/moq/moq4/blob/v4.2.1312/Samples/Lib/Moq.dll

以下来自转载 http://www.cnblogs.com/WeiGe/p/3843680.html

单元测试的目标是一次只测试一个方法,是一种细粒度的测试,但是假如某个方法依赖于其他一些难以操控的外部东东,比如说网络连接、数据库连接等时,那么我们该怎么办呢?既然单元测试的法则说不让依赖这些个外部真实的东西,那还不简单,我山寨一个不就行了吗?此时当采用以假乱真的手法来完成单元测试。实际上我们这里采用的是Mock对象,也就是真实对象的替代品,并使用Moq框架来模拟Mock对象,它为我们提供了模拟真实对象行为的能力,然后交给被测试功能使用,以此判断被测试功能是否正确。

注意:Moq只能模拟接口或抽象类。

你可以通过Nuget来获取Moq并且引用到指定的项目,也可以在google上下载,不管怎样记得在测试项目中引用Moq.dll就行~

举个栗子:

  1. public class Student
  2. {
  3. public string ID { get; set; }
  4. public string Name { get; set; }
  5. public int Age { get; set; }
  6.   }

IStudentRepository

  1. public interface IStudentRepository  
    {
  2. Student GetStudentById(string id);
  3. }

下面是方法GetStudentById的单元测试代码:

  1. [TestMethod]
  2. public void GetStudentByIdTest()
    {
  3. //创建MOCK对象
  4. var mock = new Mock<IStudentRepository>();
  5. //设置MOCK调用行为
  6. mock.Setup(p=>p.GetStudentById("1")).Returns(new Student());
  7. //MOCK调用方法
  8. mock.Object.GetStudentById("1");
  9. Assert.AreNotSame(new Student(), mock.Object.GetStudentById("1"));
  10. }

这里其实已经以假乱真了,因为真实的接口IStudentRepository里边的方法GetStudentById在实际中肯定要要访问数据库的,那么我们这里压根都么有访问什么数据库,直接用IStudentRepository接口模拟了一个对象,根本不用实现,这样瞬间就提高了单元测试的可行性。不过这里也要提个醒,就是在写代码的时候,别让代码产生过度的依赖,方可在进行单元测试时顺利进行!

说说常用的Moq成员:

1、Mock<T>:通过这个类我们能够得到一个Mock<T>对象,T可以是接口和类。它有一个公开的Object属性,这个就是我们Moq为我们模拟出的对象。

  1. var mo = new Mock<IStudentRepository>();
  2. mo.Object //其实就是模拟实现IStudentRepository接口的对象

2、It:这是一个静态类,用于过滤参数。

It很适合用来匹配数字,字符串参数,它提供了如下几个静态方法:

Is<TValue> :参数为Expression<Predict<TValue>>类型,当你需要某种类型并且这种类型要通过代码来判断的话可以使用它。

IsAny<TValue> :没有参数,只要是TValue类型的就能匹配成功。

IsInRange<TValue> :用来匹配两个的TValue类型值之间的参数。(Range参数可以设定开闭区间)

IsRegex:用正则表达式匹配。(仅限于字符串类型参数)

  1. var customer = new Mock<ICustomer>();
    customer.Setup(x => x.SelfMatch(It.Is<int>(i => i % 2 == 0))).Returns("1");//方法SelfMatch接受int型参数,当参数为偶数时,才返回字符串1。
  2. customer.Setup(p => p.SelfMatch(It.IsAny<int>())).Returns((int k) => "任何数:" + k);//方法SelfMatch接受int型,且任何int型参数都可以,然后返回:"任何数:" + k。
    customer.Setup(p => p.SelfMatch(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns("10以内的数");//方法SelfMatch接受int型,且当范围在[0,10]时,才返回10以内的数
    customer.Setup(p => p.ShowException(It.IsRegex(@"^\d+$"))).Throws(new Exception("不能是数字"));//用正则表达式过滤参数不能是数字

3、MockBehavior:用于配置MockObject的行为,比如是否自动mock。

Moq有个枚举类型MockBehavior,有三个值Strict,Loose,Default。

Strict表示Mock对象在调用一个方法前这个方法必须被Mock掉,否则就会引发MockException。而Loose与之相反,如果调用没有Mock的方法也不会出错。Default默认为Loose。例如:

  1. [TestMethod]
  2. public void MoqTest()
  3. {
  4. var mo = new Mock<ICustomer>(MockBehavior.Strict);
  5. mo.Object.Method();//在MockBehavior.Strict设置下,一切调用未填充的方法/属性/事件时会抛出异常
  6. }

4、MockFactory:Mock对象工厂,能够批量生产统一自定义配置的Mock对象,也能批量的进行Mock对象测试。

这是一个模拟对象的工厂,我们不可以成批Mock它们,例如:

  1. var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };
  2. // Create a mock using the factory settings
  3. var aMock = factory.Create<IStudent>();
  4. // Create a mock overriding the factory settings
  5. var bMock = factory.Create<ITeacher>(MockBehavior.Loose);
  6. // Verify all verifiable expectations on all mocks created through the factory
    factory.Verify();

5、Match<T>:如果你先觉得It不够用就用Match<T>,通过它能够完全自定义规则。

还是举个栗子比较能说明问题

  1. [TestMethod()]
  2. public void MoqTest()
  3. {
  4. var mo = new Mock<IRepository>();
  5. mo.Setup(p => p.Method(MatchHelper.ParamMatcher("wang"))).Returns("success");
  6. Assert.AreEqual(mo.Object.("wang"), success);
    }
  7. //此处就实现了自定义的参数匹配

  8. public static class MatchHelper
  9. {
  10. public static string ParamMatcher(string name)
  11. {
  12. return Match<string>.Create(
  13. p => p.Equals(name));
  14. }
  15. }

6、Verify和VerifyAll

用于测试mock对象的方法或属性是否被调用执行,Verify必须要先调用Verifiable()方法才能用,而VerifyAll不用这样就可以对所有的mock对象进行验证,例如:

  1. public void TestVerify()
  2. {
  3. var customer = new Mock<ICustomer>();
  4. customer.Setup(p => p.GetCall(It.IsAny<string>()))
  5. .Returns("方法调用").Verifiable();//必须调用Verifiable()方法才可以
    customer.Object.GetCall("调用了!");
  6. customer.Verify();
  7. }
  8. public void TestVerifyAll()
  9. {
  10. var customer = new Mock<ICustomer>();
  11. customer.Setup(p => p.GetCall(It.IsAny<string>()))
  12. .Returns("方法调用"); //没有显式调用Verifiable()方法也可以
    customer.Object.GetCall("调用了!");
  13. customer.VerifyAll();
  14. }

7、Callback

其实就是回调,使用Callback可以使我们在某个使用特定参数匹配的方法在被调用时得到通知。当执行某方法时,调用其内部输入的(Action)委托,例如:

  1. public void TestCallback()
    {
    var customer = new Mock<ICustomer>();
  2. customer.Setup(p => p.GetCall(It.IsAny<string>()))
  3. .Returns("方法调用")
  4. .Callback((string s)=>Console.WriteLine("ok"+s));
  5. customer.Object.GetCall("x");
    }

五、ASP.NET MVC单元测试应用

几点建议:

1、每当你向controller、service、repository层中添加一系列的新函数时,从你开始修改代码的那一刻开始,你就必须得承担有可能破坏原本正常工作的那部分功能的风险。言外之意,你必须进行单元测试才行。

2、单元测试必须是可以快速执行的。因此对于耗时的数据库交互来说,你必须对其进行mock,然后编写代码与mock的数据库进行交互

3、你不必为view进行单元测试。因为要想对view进行测试,你就不得不搭建web服务器。因为搭建web服务器相对来说很耗时,因此并不推荐针对view进行单元测试。 如果你的view包含大量复杂的逻辑,则你应当考虑将这些逻辑转移到Helper方法中。你可以针对Helper方法编写单元测试且无需搭建web服务器。

4、对于涉及到http的东东,你也必须mock一下

如何为方法添加单元测试?

1、在新建MVC项目时为项目添加默认的单元测试项目,如图所示:

2、或者在vs中相应的方法处单击鼠标右键,添加单元测试即可,如图所示:

MVC单元测试

默认生成的单元测试代码已经为Controller生成了相应的单元测试方法,例如对HomeController进行单元测试,注意测试类的命名规范,以及两个特性TestClass和TestMethod,有了这两个东东,方可对类和方法进行测试。我们可以发现是按照arrange/act/assert的模式来进行单元测试的,单元测试说白了就是三步走:arrange:初始化测试的环境属于准备阶段;act:执行测试;assert:断言,测试的结果

  1. [TestClass]
  2. public class HomeControllerTest
  3. {
  4. [TestMethod]
  5. public void About()
  6. {
  7. // Arrange
  8. HomeController controller = new HomeController();
  9. // Act
  10. ViewResult result = controller.About() as ViewResult;
  11. // Assert
  12. Assert.IsNotNull(result);
  13. }
  14.  
  15. }

难点其实在第一步,就是测试环境的准备,这里更多的是用Moq来进行模拟。另外,涉及到的Assert类主要有以下这些方法

Assert.Inconclusive()      表示一个未验证的测试;

Assert.AreEqual()           测试指定的值是否相等,如果相等,则测试通过;

AreSame()                     用于验证指定的两个对象变量是指向相同的对象,否则认为是错误

AreNotSame()                用于验证指定的两个对象变量是指向不同的对象,否则认为是错误

Assert.IsTrue()               测试指定的条件是否为True,如果为True,则测试通过;

Assert.IsFalse()              测试指定的条件是否为False,如果为False,则测试通过;

Assert.IsNull()                测试指定的对象是否为空引用,如果为空,则测试通过;

Assert.IsNotNull()           测试指定的对象是否为非空,如果不为空,则测试通过;

一个模拟访问Service服务的单元测试栗子:

  1. namespace Mvc4UnitTesting.Tests.Controllers
  2. {
  3. [TestClass]
  4. public class HomeControllerTest
  5. {
  6. [TestMethod]
  7. public void Index()
  8. {
  9. // Arrange
  10. var mockIProductService = new Mock<IProductService>();
  11. mockIProductService.Setup(p => p.GetAllProduct()).Returns(new List<Product> { new Product{ ProductId = 1, ProductName = "APPLE", Price = "5999"}});
  12. HomeController controller = new HomeController(mockIProductService.Object);
  13. // Act
  14. ViewResult result = controller.Index() as ViewResult;
  15. var product = (List<Product>)result.ViewData.Model;
  16. // Assert
  17. Assert.AreEqual("APPLE", product.First<Product>().ProductName);
  18. }
  19. }
    }

一个模拟访问Web环境的单元测试栗子:

  1. public ActionResult Index()
  2. {
  3. ViewData["Message"] = Request.QueryString["WW"];
  4. return View();
  5. }
  1. [TestMethod]
  2. public void Index()
  3. {
  4. HomeController controller = new HomeController();
  5. var httpContext = new Mock<HttpContextBase>();
  6. var request=new Mock<HttpRequestBase>();
  7. NameValueCollection queryString = new NameValueCollection();
  8. queryString.Add("WW", "WW");
  9. request.Setup(r => r.QueryString).Returns(queryString);
  10. httpContext.Setup(ht => ht.Request).Returns(request.Object);
  11. ControllerContext controllerContext = new ControllerContext();
  12. controllerContext.HttpContext = httpContext.Object;
  13. controller.ControllerContext = controllerContext;
  14. ViewResult result = controller.Index() as ViewResult;
  15. ViewDataDictionary viewData = result.ViewData;
  16. Assert.AreEqual("WW", viewData["Message"]);
  17. }

总结:

有效的测试是软件质量的保证,所以这里希望大家,包括本人自己在内,都能够把单元测试落到实处,目前对于我们来说,最大的难点在于能否恰到好处地模拟出相关的依赖资源,因此写出低耦合的代码就变得很有必要。其实多加练习使用之后,自然就能够应对相对复杂的单元测试,终有一天你会发现,单位测试只不过是分分钟的事!

net 的单元测试 初学的更多相关文章

  1. Intellij Idea系列之导Jar包与编写单元测试(二)

     Intellij Idea系列之导Jar包与编写单元测试(二) 一.初衷 对于很多的初学者来说,Intellij如何导入jar包感到很迷惑,甚至在网上搜过相关文章之后还是云里雾里,本博客通过图文并茂 ...

  2. Karma和Jasmine 自动化单元测试环境搭建

    最近初学AngularJS ,看到的一些教程中经常有人推荐使用Karma+Jasmine来进行单元测试.自己之前也对Jasmine有些了解,jasmine也是一个不错的测试框架. 1. karma介绍 ...

  3. laravel 5.6初学笔记

    laravel 5.6初学笔记 http://note.youdao.com/noteshare?id=bf4b701b49dd035564e7145ba2d978b4 框架简介 laravel文档齐 ...

  4. 白盒测试笔记之:testng 单元测试

    前言 前一篇文章我们简单了解了下单元测试的概念以及使用junit进行入门了. 但想更好做自动化测试,还是得了解下testng,毕竟,作为一名技术人,NG(下一代)的测试框架总得了解与跟进. testn ...

  5. 初学Python,需要装什么软件?

    学习Python需要安装什么软件呢?也许你是一位编程小白,还不知道如何如何安装Python软件和开发环境.那么今天我们就来学一下关于Python软件.开发环境的相关知识,希望对你有用. 学Python ...

  6. Intellij idea添加单元测试工具

    1.idea 版本是14.0.0 ,默认带有Junit,但是不能自动生成单元测试,需要下载JunitGererator2.0插件 2.Settings -Plugins,下载 JunitGenerat ...

  7. Python的单元测试(二)

    title: Python的单元测试(二) date: 2015-03-04 19:08:20 categories: Python tags: [Python,单元测试] --- 在Python的单 ...

  8. Python的单元测试(一)

    title: Python的单元测试(一) author: 青南 date: 2015-02-27 22:50:47 categories: Python tags: [Python,单元测试] -- ...

  9. DDD初学指南

    去年就打算总结一下,结果新换的工作特别忙,就迟迟没有认真动手.主要内容是很多初学DDD甚至于学习很长时间的同学没有弄明白DDD是什么,适合什么情况.这世界上没有银弹,抛开了适合的场景孤立的去研究DDD ...

随机推荐

  1. getopt解析命令行参数一例:汇集多个服务器的日志

    高效工作的一个诀窍就是尽可能自动化, 简便化. 比如, 公司里, 要搜索多个集群下的应用日志来排查问题, 需要使用 pssh: pssh -i -h api_hangzhou.iplist " ...

  2. phpcms无法读取index.html的解决步骤

    代码如下: phpcms\modules\content\classes\html.class.php 查找 复制代码 代码如下: /** * 更新首页 */ public function inde ...

  3. 欲实施CRM软件,必须先懂什么是CRM软件

    CRM是Customer Relationship Management(客户关系管理)的缩写,它是利用信息科学技术,实现市场营销.销售.服务等活动自动化,使企业能更高效地为客户提供满意.周到的服务, ...

  4. CentOS7安装Nginx-1.9.9+PHP5.6

    linux系统CentOS7 Nginx 下载地址http://nginx.org/en/download.html wget下载路径http://nginx.org/download/nginx-1 ...

  5. 一、Docker之旅

    刚刚接触到docker的同事可能会一头雾水,docker到底是一个什么东西,先看看官方的定义. Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者在笔 ...

  6. Android handle 多线程练习

    Android handle <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&quo ...

  7. python 的 from import 机制

    [A.py] from B import D class C:pass [B.py] from A import C class D:pass 为什么执行A的时候不能加载D呢? 如果将A.py改为:i ...

  8. CentOS7 续续

    1.配置网络,虚拟机为桥接模式,IP地址为 192.168.100+学号/24,配置完成后可以通过ping物理机192.168.100段,或者ping 192.168.100.140验证2.通过临时与 ...

  9. sql索引实例

    1.创建表并插入数据 在Sql Server2008中创建测试数据库Test,接着创建数据库表并插入数据,sql代码如下: USE Test IF EXISTS (SELECT * FROM INFO ...

  10. WPF+WEB+WinForm->>表现层共用类

    首先在解决方案里新建一个类库,然后在解决方案里新建三个项目,WPF,WEB,WinForm,但是这三个项目都需要一个计算类进行计算,那么就在新建的类库Calculator里面放一个Calculat.c ...