近期应项目需要,笔者疯狂学习UWP项目测试的知识,确实学习到了不少关于测试方面的知识,等有时间会好好地记录下测试的学习成果。OK,回到主题,在笔者做项目测试的过程中把自己遇到的坑都简单的记录了下来,并且写了个关于测试的Demo,传送门。下面简单地笔者的初入测试门槛的经验!
首先上环境:
- Windows 10 OS
- Windows 10 SDK 10.0.15860
- Visual Studio 2017
- MSTest Framework 1.2.0
- Etg.SimpleStubs 2.4.6
使用到的测试框架:MSTest 提供的 UnitTestFramework + 微软 Microsoft BigPark Studios
团队开发的 SimpleStub
Mock框架。
处于笔者的水平限制,笔者先后尝试了 NUnit
、XUnit
等多款测试框架但是效果并不是太好,其中 NUnit
似乎对 UnitTestProject
(单元测试应用)并不太友好(多半是笔者水平的问题,如果有Dalao能教下我NUnit与UWP的集成请私信),网上的dalao们使用 XUnit
多数是基于 ClassLibrary
的项目,而笔者尝试的时候发现系统无法直接调用 ClassLibrary
所书写的测试 ,需要加载 Runner
,有些小麻烦,不过应该也是能用的。最终,多番尝试后笔者敲定了使用 MSTest
+ SimpleStub
这两款框架(都是来自微软,相信与UWP兼容性不错)。
MSTest比较简单,上述的MSDN传送门对应的网页有详细的指导,而 SampleStub
则需要注意挺多的,笔者将自己在使用过程中踩的一些可能比较常见的坑记录了下来,下面上笔者记录的小坑:
摘自笔者Demo项目的README:
作为一个刚入测试的小菜鸡,作者在结合使用这两个框架的时候也遇到了很多坑,下面简单列举一些萌新们可能会踩的坑:
- UWP 新建一个UnitTest项目?
答:UWP 的单元测试项目优选
单元测试应用
类型的项目,即新建一个单元测试应用
的项目,并且在其中书写自己的测试
- 为什么在新建的UnitTestProject内新建测试文件不会在测试列表中显示?
答:测试文件目前使用的是注解机制,其中对作为TestFixture的测试类以及测试方法都是有访问权限要求的,即必须都为
public
的 ,并且测试方法Method
必须为public void
类型的。
测试常用的东西:
Assert
类的诸多方法,Such as:AreEqual
AreSame
IsTrue
等如何在目前的Unit测试引入Mock框架?
答:目前的情况,作者尝试过许多框架,出于作者水平,这些框架(NUnit Template 不支持、XUnit 换成Library无法识别测试)在作者的UWP上表现都不太好。目前发现表现最好的是微软去年年末推出的
SimpleStub
框架,然后作者也推荐使用这个,因为作者个人认为这个是目前与MSTest
兼容最好的Mock框架。
SimpleStub
如何使用?答:Nuget -> download SimpleStub nuget package 。然后环境就已经搭建好了,选择你的测试项目进行生成,此时框架会自动在编译期间在项目obj目录下生成一个
SimpleStub.generate.cs
的文件。利用这个文件生成一个StubXxxx
的Mock对象进而实现测试,详细用法见样例代码(UnitTestProject1
内的TestUserMagr.cs
)Tips:
- 这里补充说明下,如果项目生成后在obj目录下没找到
SimpleStub.generate.cs
文件,说明生成SimpleStub.generate.cs
文件失败了,此时应该去查看Console(输出控制台)的输出内容,挖掘出错误的原因,解决异常后重新生成下即可!
- 同时需要多留意
SimpleStub
框架为我们生成的StubXxx
Mock对象的一些方法构建,因为这与构建的Mock对象的MockBehavior
息息相关,详细请见示例代码
最后,上笔者Demo中测试的部分代码:
namespace UnitTestProject1
{
[TestClass]
public class TestUserMagr
{
// 待测试对象-> uMagr Mock对象->uMagr.UDao(IUserDao)
private UserMagr uMagr;
[TestInitialize]
public void BeforeTestInitialize()
{
uMagr = new UserMagr();
}
[TestMethod]
public void TestCallSequence()
{
// 新建mock对象
var stub = new StubIUserDao(MockBehavior.Strict)
.CreateUser((p2) => { return null == p2 ? 0 : 1; }, Times.Once)// 限定最多调用一次
.CreateUser((p2) => { return null == p2 ? 0 : 1; }, Times.Twice)// 限定最多调用两次
.CreateUser((p2) => { return null == p2 ? 0 : 1; }, Times.Forever);// 解除调用次数上限设置
// 如果不执行上述最后一行,那么stub对象只能执行CreateUser 两次(取决于最后一条的限定),执行超出次数会抛异常
// 将Mock对象赋值给依赖对象,从而解除依赖对象对测试的干预
uMagr.UDao = stub;
// 从这开始执行测试 类似 EasyMock->replayAll();
Assert.AreEqual(1, uMagr.CreateUser(new User() { Username = "123", Password = "123" }));
Assert.AreEqual(1, uMagr.CreateUser(new User() { Username = "123", Password = "123" }));
Assert.AreEqual(1, uMagr.CreateUser(new User() { Username = "123", Password = "123" }));
}
[TestMethod]
public void TestMethod_WithReturnType_WithParameters()// 测试Mock对象的函数带参数的调用
{
// 设置一些初始变量
int Expect = 1;
string Username = null;
string Password = null;
// 用于判断是否修改的对象
User user = new User() { Username = Username, Password = Password };
// 开始构建Mock
var stub = new StubIUserDao()
.CreateUser((u) => { user = u; return Expect; });
uMagr.UDao = stub;
// replayAll()
Assert.AreEqual(Expect, uMagr.CreateUser(new User() { Username = "test" }));
Assert.AreEqual("test", user.Username);
}
[TestMethod]
public void TestThatMethodStubCanBeOverwritten()// 测试Mock对象的方法是否支持重写
{
// 构建一个Mock对象
var stub = new StubIUserDao()
.CreateUser(u => 1);
// 重写其调用的方法,如果需要重写则需要加上overwrite:true,否则将不会重写
stub.CreateUser(u => 2, overwrite: true);
uMagr.UDao = stub;
// replayAll()
Assert.AreEqual(2, uMagr.CreateUser(new User()));
}
[TestMethod]
[ExpectedException(typeof(SimpleStubsException))]
public void TestMethod_WithReturnType_WithParameters_DefaultBehavior_Strict()// 展示 MockBehavior为Strict下的不同之处,于下面的method一起比对观察
{
// 构建一个不带函数定义的Mock对象(存根)
var stub = new StubIUserDao(MockBehavior.Strict);
uMagr.UDao = stub;
// 调用方法的时候,Strict模式下会抛出异常,因为没有定义调用函数
Assert.AreEqual(0, uMagr.CreateUser(null));
}
[TestMethod]
public void TestMethod_Void_WithNoParameters_DefaultBehavior_Loose()
{
// 构建一个不带函数定义的Mock对象(存根),默认下MockBehavior为Loose
var stub = new StubIUserDao();
uMagr.UDao = stub;
// 调用方法的时候,Loose模式下不会抛出异常,并且返回函数返回值的默认值 null | 默认基本类型值 |
Assert.AreEqual(0, uMagr.CreateUser(null));
}
}
}
OK,就先记录到这!
附:感谢多位dalao超棒的回答:
- In Github:
- In StackOverFlow:
来源:oschina
链接:https://my.oschina.net/u/3744313/blog/1808554