在Xunit中使用FsCheck
目录
编写基于Property-based的单元测试
使用FsCheck编写Property-based测试
在Xunit中使用FsCheck
使用FsCheck编写Model-based测试-待续
无论是Xunit还是Nunit都有额外的扩展用来编写FsCheck测试,以Xunit为例 :
Install-Package FsCheck.Xunit -Version 2.13.0
不同于普通的Xunit测试,一般的测试需要标记[Fact],你需要使用[Property]标记FsCheck测试。给定一个函数:
private int Add(int x, int y)
{
return x + y;
}
针对加法交换律编写一个Property-based测试:
[Property]
public bool Commutative(int x, int y)
{
return Add(x, y) == Add(y, x);
}
F#
[<Property>]
let Commutative x y =
add x y = add y x
在之前的例子里,我们介绍了什么是Property-based测试,然后花了一篇博客介绍了各种各样的Generator,每一个刚开始了解Property-based测试的人都会觉得这种方案很有意思,但是当你真正开始编写Property-base测试的时候你就会感觉得无从下手,应该断言什么样的Properties呢?
这篇文章介绍一些Properties供你参考:
1. 不同的执行顺序,同样的执行结果
例如被测函数为List.OrderBy,如果我们在List.OrderBy函数之前执行一个操作Add1,然后执行List.OrderBy函数。结果应该等于先执行List.OrderBy函数再执行操作Add1
[Property]
public bool AddOneThenSortShouldSameAsSortThenAddOne(List<int> list)
{
var result1 = list.OrderBy(x => x).Select(Add1);
var result2 = list.Select(Add1).OrderBy(x => x);
return result1.SequenceEqual(result2);
}
F#
[<Property(Verbose=true)>]
let ``+1 then sort should be same as sort then +1`` aList =
let add1 x = x + 1
let result1 = aList |> List.sort |> List.map add1
let result2 = aList |> List.map add1 |> List.sort
result1 = result2
2.连续执行操作,结果跟之前一致
例如List.Reverse函数,连续执行两次,结果跟期初是一样的。类似的函数如序列化和反序列化,Redo和Undo。
[Property]
public bool ReverseThenReverseShouldSameAsOriginal(int[] list)
{
var result= list.Reverse().Reverse();
return result.SequenceEqual(list);
}
F#
[<Property>]
let ``reverse then reverse should be same as original``
(aList:int list) =
let reverseThenReverse = aList |> List.rev |> List.rev
reverseThenReverse = aList
3. 有一些属性是永远不会改变的
在数据变化过程中,有一些属性是永远不会改变的,例如Sort操作,前后数据的Length总是不变的,这一属性可以作为Property-based测试的一个依据:
public bool SomethingNeverChanged(List<int> list)
{
var result = list.OrderBy(x => x);
return result.Count() == list.Count;
}
F#
let ``sort should have same length as original`` (aList:int list) =
let sorted = aList |> List.sort
List.length sorted = List.length aList
为OO代码编写Property-based测试
接下来我们尝试针对一个OO的例子编写Property-based测试:
public class Dollar
{
private int _amount;
public Dollar(int amount)
{
_amount = amount;
}
public int Amount => _amount;
public void Add(int add)
{
_amount = _amount + add;
}
public void Multiplier(int multiplier)
{
_amount = _amount * multiplier;
}
public static Dollar Create(int amount)
{
return new Dollar(amount);
}
}
F#
type Dollar(amount : int) =
let mutable privateAmount = amount;
member this.Amount = privateAmount
member this.Add add =
privateAmount <- this.Amount + add
member this.Times multiplier =
privateAmount <- this.Amount * multiplier
static member Create amount =
Dollar amount
Dollar类主要有两个方法,Add和Multiplier分别用来修改私有变量_amount。如何测试Dollar类呢?都有哪些Properties可用?调用Add方法后再读取Amount的值应该是同一个值:
[Property]
public bool SetAndGetShouldGiveSameResult(int amount)
{
var dollar = Dollar.Create(0);
dollar.Add(amount);
return dollar.Amount == amount;
}
F#
[<Property>]
let ``set then get should give same result`` value =
let obj = Dollar.Create 0
obj.Add value
let newValue = obj.Amount
value = newValue
还有什么Property可供使用呢,Add和Multiplier两个方法执行完毕的结果等价于直接Create:
[Property]
public bool AddThenMultiplierSameAsCreate(int start, int times)
{
var dollar = Dollar.Create(0);
dollar.Add(start);
dollar.Multiplier(times);
var dollar2 = Dollar.Create(start * times);
return dollar.Amount == dollar2.Amount;
}
F#
[<Property>]
let ``add then multiplier same as create`` value times =
let dollar = Dollar.Create 0
dollar.Add value
dollar.Times times
let dollar2 = Dollar.Create(value*times);
dollar.Amount = dollar2.Amount
编写自定义Generator
迄今为止,我们都在使用FsCheck自带的Generator,而在实际项目开发过程中,你还需要生成自定义的Generator供你使用,例如有一个User类型:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
自定义Generator:
public class UserArbitrary: Arbitrary<User>
{
public override Gen<User> Generator =>
from x in Arb.Generate<string>()
from int y in Gen.Choose(20, 30)
where x != string.Empty
select new User {Name = x, Age = y};
}
最后还要将自定义的Arbitrary注册在FsCheck中:
public class MyGenerators {
public static Arbitrary<User> User() {
return new UserArbitrary();
}
}
Arb.Register<MyGenerators>();
写个例子试试:
[Property]
public bool GenerateUsers(User user)
{
return user.Name != string.Empty;
}
所以代码实例均可以在github下载
最新文章
- 如何修改geditor的配置文件 -好像geditor没有文本格式的配置文件? 要使用dconf-editor来配置- geditor自己配置编码格式
- 【转载】OpenGL超级宝典笔记——GLSL语言基础
- java序列化---转
- cygwin的rebaseall失败
- mongodb 操作语句与sql操作语句对比
- Qt 添加外部库文件(四种方法)
- linq学习三个实例
- Raspberry pi raspbain系统下使用vim
- wordpress建站过程1
- shell脚本字符串截取
- git checkout+文件丢失
- Python TypeError: not all arguments converted during string formatting ——元组tuple(a)和(a,)的区别
- 关于vue打包是因代码校验报错
- .NetCore WebApi 添加 Log4Net
- Git命令面试集
- 最短路径之Floyd-warshall算法
- python set 集合复习--点滴
- uniDAC的安装和使用
- webpack的版本进化史
- Linux下高并发socket最大连接数各种限制的调优
热门文章
- BZOJ_5055_膜法师_树状数组+离散化
- [SDOI2011]染色 BZOJ2243 树链剖分+线段树
- 微信小程序中placeholder的样式
- ASP.NET Core的实时库: SignalR -- 预备知识
- C#判断字符串是不是英文或数字
- Redis--Memched--Cache缓存介绍使用
- asp.net core系列 60 Ocelot 构建服务认证示例
- 约定Service构建方式
- 第6章 令牌撤销端点(Token Revocation Endpoint) - IdentityModel 中文文档(v1.0.0)
- I/O基础之概念