Rino Mock的使用
通过Rino Mock来模拟接口并返回数据,验证客户端数据处理的正确性。
2 public void Value_Test()
3 {
4 IBugInfoDataProvider dataProvider = MockRepository.GenerateMock<IBugInfoDataProvider>();
5 dataProvider.Stub(n => n.SearchMembers()).Return(new Member[] {
6 new Member{MemberID=1,Name="Peter"}
7 });
8
9 MemberControl ctrl = new MemberControl();
10 ctrl.BugDataProvider = dataProvider;
11
12 TestForm form = new TestForm();
13
14 form.AddControl(ctrl);
15 form.AddClickEvent("check value", (s, e) => {
16 Assert.AreEqual(ctrl.SelectedMemeber.MemberID, 1);
17 });
18
19 form.ShowDialog();
20 }
21
22 [TestMethod]
23 public void Empty_Test()
24 {
25 IBugInfoDataProvider dataProvider = MockRepository.GenerateMock<IBugInfoDataProvider>();
26 dataProvider.Stub(n => n.SearchMembers()).Return(new Member[] {});
27
28 MemberControl ctrl = new MemberControl();
29 ctrl.BugDataProvider = dataProvider;
30
31 TestForm form = new TestForm();
32
33 form.AddControl(ctrl);
34 form.AddClickEvent("empty null", (s,e) => {
35 Assert.IsTrue(ctrl.SelectedMemeber == null);
36 });
37
38 form.ShowDialog();
39 }
第一个测试目的是为了测试客户端控件MemberControl上SelectedMember返回值的正确性。
第二个测试目的是为了测试客户端控件能够正确处理接口返回的数据集长度为0的情况。
以上测试主要应用于验证客户端加载数据的业务逻辑,实际上就是数据使用者和数据提供者(接口)之间的互交测试。精简如下图。
实际上的情况要更加复杂,我们可能会在更大的范围内来对客户端的业务逻辑进行测试或者对客户端进行测试驱动开发。如下图:
我们会先对BugInfoControl, DocControl, FlowControl, StateHistoryControl和MemberControl进行测试驱动开发,即会在BugInfoControl_Test,DocControl_Test,FlowControl_Test等一系列测试文件中Mock IBugInfoDataProvider的不同接口方法中的数据。
如果测试驱动的范围扩大到了BugControl,那么按照之前的思路,会在BugControl_Test中Mock IBugInfoDataProvider的相关数据。这显然需要耗费掉团队成员的大量精力。我们得想办法改变现状。
通常来说,解决问题的有效办法大多数也是很简单的方法,我在这里想到的是继承,我打算抛弃RinoMock。思路如下图
针对不同的测试,可以准备IBugInfoDataProvider的不同实现类,而且可以让这些实现类之间是继承关系,如下图。
通过这种策略,我们可以让在MemberControl_Test使用的测试数据在BugControl_Test中同样有用,从而达到系统的,有步骤的实现测试数据准备的过程。
当我准备重构当前的测试代码时,发现RinoMock还是有必要使用到的,不能一竿子将其打死。
例如最初的关于MemberControl的测试代码,对于ValidValue_Test。由于它测试的是在正常数据上下文中,MemberControl的行为,所以我们可以使用BugInfoDataProvider_BasicData类来对其进行测试。
{
UnityContainer container = new UnityContainer();
container.RegisterType<IBugInfoDataProvider, BugInfoDataProvider_BasicData>();
MemberControl ctrl = container.Resolve<MemberControl>();
TestForm form = new TestForm();
form.AddControl(ctrl);
form.AddClickEvent("check value", (s, e) => {
Assert.AreEqual(ctrl.SelectedMemeber.MemberID, 1);
});
form.ShowDialog();
}
对于Empty_Test,由于它测试的是极端情况,所以仍然保留RinoMock来提供个别特殊情况下的数据。