一个简单的测试写好了。由于我们使用的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; } } |
新建一个类,用来测试。这个Create会使用仓储操作数据库。这里不希望实际操作数据库,以达到快速测试执行。
[Fact] public void Create_Ok() { IStudentRepositories studentRepositories = new StubStudentRepositories(); StudentService service = new StudentService(studentRepositories); var isCreateOk = service.Create(null); Assert.True(isCreateOk); } public class StubStudentRepositories : IStudentRepositories { public void Add(Student model) { } } |
每次做类似的操作都要手动建议StubStudentRepositories存根,着实麻烦。好在Mock框架(Moq)可以自动帮我们完成这个步骤。
例:
[Fact] public void Create_Mock_Ok() { var studentRepositories = new Mock<IStudentRepositories>(); var notiy = new Mock<Notiy>(); StudentService service = new StudentService(studentRepositories.Object); var isCreateOk = service.Create(null); Assert.True(isCreateOk); } |
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。