HTML5技术

C#单元测试,带你快速入门 - 农码一生

字号+ 作者:H5之家 来源:H5之家 2017-05-18 16:00 我要评论( )

C#单元测试,带你快速入门 注:本文示例环境 为什么要编写单元测试 对于为什么要编写单元测试,我想每个人都有着自己的理由。对于我个人来说,主要是为了方便修改(bug修复)而不引入新的问题。可以放心大胆的重构,我认为重构觉得是提高代码质量和提升个人

C#单元测试,带你快速入门

注:本文示例环境

为什么要编写单元测试

对于为什么要编写单元测试,我想每个人都有着自己的理由。对于我个人来说,主要是为了方便修改(bug修复)而不引入新的问题。可以放心大胆的重构,我认为重构觉得是提高代码质量和提升个人编码能力的一个非常有用的方式。好比一幅名画一尊雕像,都是作者不断重绘不断打磨出来的,而优秀的代码也需要不断的重构。
当然好处不仅仅如此。TDD驱动,使代码更加注重接口,迫使代码减少耦合,使开发人员一开始就考虑面对各种情况编写代码,一定程度的保证的代码质量,通过测试方法使后续人员快速理解代码...等。
额,至于不写单元测试的原因也有很多。原因无非就两种:懒、不会。当然你还会找更多的理由的。

框架选型

至于框架的选型。其实本人并不了解也没写过单元测试,这算是第一次真正接触吧。在不了解的情况下怎么选型呢?那就是看哪个最火、用的人多就选哪个。起码出了问题也容易同别人交流。

基本概念
  • AAA逻辑顺序
  • 准备(Arrange)对象,创建对象,进行必要的设置
  • 操作(Act)对象
  • 断言(Assert)某件事情是预期的。
  • Assert(断言):对方法或属性的运行结果进行检测
  • Stub(测试存根\桩对象):用返回指定结果的代码替换方法(去伪造一个方法,阻断对原来方法的调用,为了让测试对象可以正常的执行)
  • Mock(模拟对象):一个带有期望方法被调用的存根(可深入的模拟对象之间的交互方式,如:调用了几次、在某种情况下是否会抛出异常。mock是一种功能丰富的stub)
    Stub和Mock的定义比较抽象不好理解,延伸阅读1、阅读2、阅读3
  • 好的测试
  • UnitOfWorkName  被测试的方法、一组方法或者一组类
  • Scenario  测试进行的假设条件,例如“登入失败”,“无效用户”或“密码正确”等
  • ExpectedBehavior  在测试场景指定的条件下,你对被测试方法行为的预期  
  • 基础实践

    “废话”说的够多了,下面撸起袖子开干吧。
    下面开始准备工作:

  • 对类库 TestDemo.Tests 用nuget 安装XUnit 2.2.0、xunit.runner.visualstudio 2.2.0、Moq 4.7.10。
  • 添加 TestDemo.Tests 对 TestDemo 的引用。
  • 例:

    public class Arithmetic { public int Add(int nb1, int nb2) { return nb1 + nb2; } }

    对应的单元测试:(需要导入using Xunit;命名空间。 )

    public class Arithmetic_Tests { [Fact]//需要在测试方法加上特性Fact public void Add_Ok() { Arithmetic arithmetic = new Arithmetic(); var sum = arithmetic.Add(1, 2); Assert.True(sum == 3);//断言验证 } }

    一个简单的测试写好了。由于我们使用的vs2017 它出了一个新的功能“Live Unit Testing”,我们可以启用它进行实时的测试。也就是我们编辑单元测试,然后保存的时候,它会自动生成自动测试,最后得出结果。



    我们看到了验证通过的绿色√。
    注意到测试代码中的参数和结果都写死了。如果我们要对多种情况进行测试,岂不是需要写多个单元测试方法或者进行多次方法执行和断言。这也太麻烦了。在XUnit框架中为我们提供了Theory特性。使用如下:
    例:

    [Theory] [InlineData(2, 3, 5)] [InlineData(2, 4, 6)] [InlineData(2, 1, 3)] //对应测试方法的形参 public void Add_Ok_Two(int nb1, int nb2, int result) { Arithmetic arithmetic = new Arithmetic(); var sum = arithmetic.Add(nb1, nb2); Assert.True(sum == result); }


    测试了正确的情况,我们也需要测试错误的情况。达到更好的覆盖率。
    例:

    [Theory] [InlineData(2, 3, 0)] [InlineData(2, 4, 0)] [InlineData(2, 1, 0)] public void Add_No(int nb1, int nb2, int result) { Arithmetic arithmetic = new Arithmetic(); var sum = arithmetic.Add(nb1, nb2); Assert.False(sum == result); }

    有时候我们需要确定异常
    例:

    public int Divide(int nb1, int nb2) { if (nb2==0) { throw new Exception("除数不能为零"); } return nb1 / nb2; } [Fact] public void Divide_Err() { Arithmetic arithmetic = new Arithmetic(); Assert.Throws<Exception>(() => { arithmetic.Divide(4, 0); });//断言 验证异常 }

    以上为简单的单元测试。接下来,我们讨论更实际更真实的。
    我们一般的项目都离不开数据库操作,下面就来实践下对EF使用的测试:

  • 使用nuget安装 EntityFramework 5.0.0
  • 例:

    public class StudentRepositories { //... public void Add(Student model) { db.Set<Student>().Add(model); db.SaveChanges(); } } [Fact] public void Add_Ok() { StudentRepositories r = new StudentRepositories(); Student student = new Student() { Id = 1, Name = "张三" }; r.Add(student); var model = r.Students.Where(t => t.Name == "张三").FirstOrDefault(); Assert.True(model != null); }

    我们可以看到我们操作的是EF连接的实际库。(注意:要改成专用的测试库)
    我们会发现,每测试一次都会产生对应的垃圾数据,为了避免对测试的无干扰性。我们需要对每次测试后清除垃圾数据。

    //注意:测试类要继承IDisposable接口 public void Dispose() { StudentRepositories r = new StudentRepositories(); var models = r.Students.ToList(); foreach (var item in models) { r.Delete(item.Id); } }

    这样每执行一个测试方法就会对应执行一次Dispose,可用来清除垃圾数据。
    我们知道对数据库的操作是比较耗时的,而单元测试的要求是尽可能的减少测试方法的执行时间。因为单元测试执行的比较频繁。基于前面已经对数据库的实际操作已经测试过了,所以我们在后续的上层操作使用Stub(存根)来模拟,而不再对数据库进行实际操作。
    例:
    我们定义一个接口IStudentRepositories 并在StudentRepositories 继承。

    public interface IStudentRepositories { void Add(Student model); } public class StudentRepositories: IStudentRepositories { //省略。。。 (还是原来的实现) } public class StudentService { IStudentRepositories studentRepositories; public StudentService(IStudentRepositories studentRepositories) { this.studentRepositories = studentRepositories; } public bool Create(Student student) { studentRepositories.Add(student); return true; } }

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 使用测试用例来约束自己的代码 - 陈宏鸿

      使用测试用例来约束自己的代码 - 陈宏鸿

      2017-05-09 14:00

    • Android -- 带你从源码角度领悟Dagger2入门到放弃(一) - 阿呆哥哥

      Android -- 带你从源码角度领悟Dagger2入门到放弃(一) - 阿呆哥哥

      2017-04-21 11:02

    • 程序员带你一步步分析AI如何玩FlappyBird - yhthu

      程序员带你一步步分析AI如何玩FlappyBird - yhthu

      2017-04-13 09:03

    • 火狐手机测试 - yangzailu1990

      火狐手机测试 - yangzailu1990

      2017-03-14 15:00

    网友点评
    r