当前位置:首页 > 主打产品 >

ASP.NET MVC之单元测试分分钟的事

编辑:北京聚贤贵都宾馆有限公司时间:2017-09-05 12:28:58阅读次数:2
ASP.NET MVC之单元测试分分钟的事 一、为什么要进行单元测试?

大部分开发者都有个习惯(包括本人在内),常常不喜欢去做单元测试。因为我们对自己写的程序总是盲目自信,或者存在侥幸心理每次运行通过后就直接扔给测试组的妹子们了。结果妹子一测,大把大把的bug出现了,最后每每看到测试的妹子走过来,心里就只想说一句话:你是猴子请来的逗比吗?本来想节省时间,结果最后花在找BUG和修复BUG的这些时间加起来已经比开发这个模块所花的时间还要多了,最后更要命的是,坑爹的加班就在所难免了!如果一开始将bug遏制在萌芽状态,我们至于这么苦逼吗?SO,单元测试很有必要!

二、单元测试法则

1、单元测试必须能够重复执行,就是能够非常频繁地执行

2、单元测试的执行速度不能太慢,要不然会影响开发进度的

3、单元测试不应该依赖于外部资源和真实的环境

4、单元测试不应该涉及到真实数据库的操作

5、要确保单元测试的可信度

6、单元测试通常以测试一个方法为单位

7、每一个程序猿都需要为自己写的代码编写单元测试代码

三、单元测试工具

我在这里仅仅推荐一个比较实用的测试工具NUnit,可单独使用,也可以通过TestDriven.NET(TestDriven.NET是以插件形式集成在Visual Studio IDE中的单元测试工具,完全兼容所有.NET Framework版本,并且集成了多种单元测试框架诸如NUnit,MbUnit,以及 MS Team System 等)将其加入到vs中。

NUnit作为xUnit家族中的.Net成员,是.NET的单元测试框架,xUnit是一套适合于多种语言的单元测试工具。它具有如下特征:

提供了API,使得我们可以创建一个带有“通过/失败”结果的重复单元。

包括了运行测试和表示结果所需的工具。

允许多个测试作为一个组在一个批处理中运行。

非常灵巧,操作简单,我们花费很少的时间即可学会并且不会给测试的程序添加额外的负担。

功能可以扩展,如果希望更多的功能,可以很容易的扩展它。

套用老罗的话就是一句话:它是当今.NET领域最牛逼的测试工具之一

在.NET下的单元测试工具其实非常多,这里不想多说,我们就使用微软自己提供的测试框架Unit Test Framework,已经集成在vs中了~

四、MOQ

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

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

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

举个栗子:

复制代码

public class Student

{

public string ID { get; set; }

public string Name { get; set; }

public int Age { get; set; }

  }

复制代码

IStudentRepository

public interface IStudentRepository  

{

Student GetStudentById(string id);

}

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

复制代码

[TestMethod]

public void GetStudentByIdTest()

{

//创建MOCK对象

var mock = new Mock<IStudentRepository>();

//设置MOCK调用行为

mock.Setup(p=>p.GetStudentById("1")).Returns(new Student());

//MOCK调用方法

mock.Object.GetStudentById("1");

Assert.AreNotSame(new Student(), mock.Object.GetStudentById("1"));

}

复制代码

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

说说常用的Moq成员:

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

var mo = new Mock<IStudentRepository>();

mo.Object //其实就是模拟实现IStudentRepository接口的对象

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

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

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

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

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

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

复制代码

var customer = new Mock<ICustomer>();

customer.Setup(x => x.SelfMatch(It.Is<int>(i => i % 2 == 0))).Returns("1");//方法SelfMatch接受int型参数,当参数为偶数时,才返回字符串1。

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。例如:

复制代码

[TestMethod]

public void MoqTest()

{

var mo = new Mock<ICustomer>(MockBehavior.Strict);

mo.Object.Method();//在MockBehavior.Strict设置下,一切调用未填充的方法/属性/事件时会抛出异常

}

复制代码

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

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

复制代码

var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };

// Create a mock using the factory settings

var aMock = factory.Create<IStudent>();

// Create a mock overriding the factory settings

var bMock = factory.Create<ITeacher>(MockBehavior.Loose);

// Verify all verifiable expectations on all mocks created through the factory

factory.Verify();

复制代码

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

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

复制代码

[TestMethod()]

public void MoqTest()

{

var mo = new Mock<IRepository>();

mo.Setup(p => p.Method(MatchHelper.ParamMatcher("wang"))).Returns("success");

Assert.AreEqual(mo.Object.("wang"), “success);

}

//此处就实现了自定义的参数匹配

public static class MatchHelper

{

public static string ParamMatcher(string name)

{

return Match<string>.Create(

p => p.Equals(name));

}

}

复制代码

6、Verify和VerifyAll

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

复制代码

public void TestVerify()

{

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法调用").Verifiable();//必须调用Verifiable()方法才可以

customer.Object.GetCall("调用了!");

customer.Verify();

}

public void TestVerifyAll()

{

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法调用"); //没有显式调用Verifiable()方法也可以

customer.Object.GetCall("调用了!");

customer.VerifyAll();

}

复制代码

7、Callback

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

复制代码

public void TestCallback()

{

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法调用")

.Callback((string s)=>Console.WriteLine("ok"+s));

customer.Object.GetCall("x");

}

复制代码

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

几点建议:

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

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

企业建站2800元起,携手武汉肥猫科技,做一个有见地的颜值派!更多优惠请戳:武汉建网站 http://www.feimao666.com

上一篇:Android ColorFilter and Tint 下一篇:最后一页

相关阅读