模拟对象测试技术Mock(一)
因为目前项目时间比较紧(一个月的开发周期),并且,工作流的测试步骤也比较多,所以,我决定采用一种模拟对象测试技术:Mock。Mock可以为接口生成模拟对象,从而在我们写实现代码之前就检查其逻辑和交互行为是否正确。我们选用的Mock框架是Moq 3,基于lambda语法的一款非常简单的Mock框架。
下面使用示例向大家介绍Mock测试框架的好处:
1 IEmployee.cs
2 namespace Thoth.FeatureProject
3 {
4 public interface IEmployee
5 {
6 int Identifier { get; set; }
7 string Name { get; set; }
8 int Age { get; set; }
9 }
10 }
2 namespace Thoth.FeatureProject
3 {
4 public interface IEmployee
5 {
6 int Identifier { get; set; }
7 string Name { get; set; }
8 int Age { get; set; }
9 }
10 }
1 ISeat.cs
2 namespace Thoth.FeatureProject
3 {
4 public interface ISeat
5 {
6 IEmployee Get(int identifier);
7 }
8 }
2 namespace Thoth.FeatureProject
3 {
4 public interface ISeat
5 {
6 IEmployee Get(int identifier);
7 }
8 }
1 MainTest.cs
2
3 using Moq;
4 using NUnit.Framework;
5 using Thoth.FeatureProject;
6
7 namespace Thoth.MoqProject
8 {
9 [TestFixture]
10 public class MainTest
11 {
12 [Test]
13 public void EmployeeTest()
14 {
15 var employee = new Mock<IEmployee>();
16 employee.SetupGet(p => p.Identifier).Returns(1);
17 employee.SetupGet(p => p.Name).Returns("Justina Chen");
18 employee.SetupGet(p => p.Age).Returns(28);
19 Assert.AreEqual("Justina Chen", employee.Object.Name);
20
21 var seat = new Mock<ISeat>();
22 seat.Setup(p => p.Get(1)).Returns(employee.Object);
23 var seatReturned = seat.Object.Get(1);
24 Assert.AreEqual("Justina Chen", seatReturned.Name);
25
26 var seatI = new Mock<ISeat>();
27 seatI.Setup(p => p.Get(It.IsAny<int>())).Returns(employee.Object);
28 var seatNewReturned = seatI.Object.Get(1);
29 Assert.AreEqual("Justina Chen", seatNewReturned.Name);
30
31 var seatII = new Mock<ISeat>();
32 seatII.Setup(p => p.Get(It.Is<int>(id => id > 0 && id < 6))).Returns(employee.Object);
33 var seatNewConditionReturned = seatII.Object.Get(1);
34 Assert.AreEqual("Justina Chen", seatNewConditionReturned.Name);
35 }
36 }
37 }
38
2
3 using Moq;
4 using NUnit.Framework;
5 using Thoth.FeatureProject;
6
7 namespace Thoth.MoqProject
8 {
9 [TestFixture]
10 public class MainTest
11 {
12 [Test]
13 public void EmployeeTest()
14 {
15 var employee = new Mock<IEmployee>();
16 employee.SetupGet(p => p.Identifier).Returns(1);
17 employee.SetupGet(p => p.Name).Returns("Justina Chen");
18 employee.SetupGet(p => p.Age).Returns(28);
19 Assert.AreEqual("Justina Chen", employee.Object.Name);
20
21 var seat = new Mock<ISeat>();
22 seat.Setup(p => p.Get(1)).Returns(employee.Object);
23 var seatReturned = seat.Object.Get(1);
24 Assert.AreEqual("Justina Chen", seatReturned.Name);
25
26 var seatI = new Mock<ISeat>();
27 seatI.Setup(p => p.Get(It.IsAny<int>())).Returns(employee.Object);
28 var seatNewReturned = seatI.Object.Get(1);
29 Assert.AreEqual("Justina Chen", seatNewReturned.Name);
30
31 var seatII = new Mock<ISeat>();
32 seatII.Setup(p => p.Get(It.Is<int>(id => id > 0 && id < 6))).Returns(employee.Object);
33 var seatNewConditionReturned = seatII.Object.Get(1);
34 Assert.AreEqual("Justina Chen", seatNewConditionReturned.Name);
35 }
36 }
37 }
38
上面的测试全部通过。
大家注意到了吗,我只写了两个接口,而创建Mock对象之后,我就可以以实例的方式测试对象的行为了,如果不正确,我们这时就可以对接口的设计进行修改。下面是注释:
1 //创建一个Mock对象(泛型)
2 var employee = new Mock<IEmployee>();
3
4 //为此Mock对象的一个属性(这行是针对Identifier)值(SetupGet是为接口的Property设置值,值在Return()方法中)
5 employee.SetupGet(p => p.Identifier).Returns(1);
6
7 //取出Mock对象的值,检查是否正确
8 Assert.AreEqual("Justina Chen", employee.Object.Name);
9
10 //为Mock对象的方法设置一个参数及返回对象(Setup针对的是方法),Returns是返回对象,Get(1)中值为1的参数
11 seat.Setup(p => p.Get(1)).Returns(employee.Object);
12
13 //这两行检查我们返回结果是否正确
14 var seatReturned = seat.Object.Get(1);
15 Assert.AreEqual("Justina Chen", seatReturned.Name);
16
17 //这行是指任何int参数都返回此对象
18 seat.Setup(p => p.Get(It.IsAny<int>())).Returns(employee.Object);
19
20 //这里指定了一个筛选条件
21 seat.Setup(p => p.Get(It.Is<int>(id => id > 0 && id < 6))).Returns(employee.Object);
2 var employee = new Mock<IEmployee>();
3
4 //为此Mock对象的一个属性(这行是针对Identifier)值(SetupGet是为接口的Property设置值,值在Return()方法中)
5 employee.SetupGet(p => p.Identifier).Returns(1);
6
7 //取出Mock对象的值,检查是否正确
8 Assert.AreEqual("Justina Chen", employee.Object.Name);
9
10 //为Mock对象的方法设置一个参数及返回对象(Setup针对的是方法),Returns是返回对象,Get(1)中值为1的参数
11 seat.Setup(p => p.Get(1)).Returns(employee.Object);
12
13 //这两行检查我们返回结果是否正确
14 var seatReturned = seat.Object.Get(1);
15 Assert.AreEqual("Justina Chen", seatReturned.Name);
16
17 //这行是指任何int参数都返回此对象
18 seat.Setup(p => p.Get(It.IsAny<int>())).Returns(employee.Object);
19
20 //这里指定了一个筛选条件
21 seat.Setup(p => p.Get(It.Is<int>(id => id > 0 && id < 6))).Returns(employee.Object);
1 //那么,下面的测试结果是?
2 var seatIII = new Mock<ISeat>();
3 seatIII.Setup(p => p.Get(1)).Returns(employee.Object);
4 var seatObject = seatIII.Object.Get(0);
5 Assert.AreEqual("Justina Chen", seatObject.Name);
2 var seatIII = new Mock<ISeat>();
3 seatIII.Setup(p => p.Get(1)).Returns(employee.Object);
4 var seatObject = seatIII.Object.Get(0);
5 Assert.AreEqual("Justina Chen", seatObject.Name);
seatObject.Name将报出NullRefenrenceException,因为我们Setup的是1,而希望获取的对象是0,所以,检索为空 :-)
我们项目中目前运用到的Mock技术差不多就是这样了,希望能减轻大家跑流程的步骤 :-D 随着使用的深入,会继续与大家分享。