實際工作中我們需要的數據邏輯萬千,千變萬化,而AutoFixture默認是按照一定算法隨機生成一些假數據,雖然這在多數時候是ok的,但是可能不能滿足我們的所有業務場景,有些時候我們需要進行一些配置,以期達到指定目標.
AutoFixture簡單使用
前面我介首先介紹的是AutoFixture如何與Nunit結合提供測試數據,這里我們介紹一下它自身,即脫離Nunit時它是如何工作起來的.
這里主要用到的就是Fixture對象的Create
看以下代碼
[Test]
public void FixValueTest()
{
var fix = new Fixture();
var str = fix.Create<string>();
}
通過以上代碼,我們就可能創建一個string類型的對象,其它對象也是如法炮制.
下面我們來解決上一節中遺漏的一個問題,就是如何在創建集合的時候顯式的指定個數.
其實也很簡單,那就是在創建Fixture對象的時候指定一個RepeatCount,這樣就可以生成指定數量的集合啦.
代碼改為如下
[Test]
public void FixValueTest()
{
var fix = new Fixture {RepeatCount = 10};
var str = fix.Create<IEnumerable<string>>();
}
就可以生成一個包含10個String元素的集合.
很多時候我們並不是簡單的創建一個字符串或者數字,而是創建一個對象,很多時候我們要是對這些對象進行驗證的,如果隨機生成一些可能無法通過驗證,我們下面介紹如何按照一定的規則生成一個對象.
比如說我們要生成一個Person對象,服務器對Person的Name是要約束的,不能包含特定符號和阿拉伯數字,而AutoFixture自動生成的則是Guid轉成的字符字符串,並且長度也不符合姓名規則.
下面我們看一下如何生成一個例規的姓名.
[Test]
public void FixValueTest()
{
var s = GetString(5);
var fix = new Fixture();
fix.Customizations.Add(new StringGenerator(() => s));
var person= fix.Create<Person>();
}
string GetString(int count)
{
List<int> ints = new List<int>();
Random rand = new Random();
for (int i = 0; i < count; i++)
{
int value = rand.Next(97 ,122);
ints.Add(value);
}
var charArr = ints.Select(Convert.ToChar).ToArray();
var str = string.Concat(charArr);
return str;
}
這里我們自定義了一個算法,生成一個字符串,然后在fix配置里的自定義配置里面添加一個StringGenerator自定義配置類(這個類是框架帶的),它接收一個委托.這樣我們就可以得到期待的字符串了.
我們把測試代碼改為如下
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new StringSpecimenBuilder());
var person= fix.Create<Person>();
}
這里的StringSpecimenBuilder是我們自定義的,它實現了ISpecimenBuilder接口,我們看下代碼
public class StringSpecimenBuilder:ISpecimenBuilder
{
private readonly int _strLenCount;
public StringSpecimenBuilder(int strLenCount=5)
{
_strLenCount = strLenCount;
}
public object Create(object request, ISpecimenContext context)
{
var property = request as PropertyInfo;
if (property != null &&
property.Name == "Name" &&
property.PropertyType == typeof(string))
return GetString(_strLenCount);
return new NoSpecimen();
}
string GetString(int count)
{
List<int> ints = new List<int>();
Random rand = new Random();
for (int i = 0; i < count; i++)
{
int value = rand.Next(97, 122);
ints.Add(value);
}
var charArr = ints.Select(Convert.ToChar).ToArray();
var str = string.Concat(charArr);
return str;
}
}
其中的GetString我們剛才用到過,這里把它移到這里來.
我們來分析下這段代碼,構造函數里我們接收一個int類型變量,用於自定義生成字符串的長度.
下面的Create方法為從接口里實現來的方法.
它的第一個參數request為要創建的對象,對於我們的Person類來說,它要創建這個類和類里的所有屬性,每一個屬性都是一個request對象.下面的代碼我們判斷請求對象是否是屬性,如果是並且屬性名是Name並且屬性類型為string,那么我們就返回算法得到的值,否則返回NoSpecimen,返回NoSpecimen表示不使用自定義的算法.
通過以上配置生成的name就能符合我們的需求了.
[info]在集成測試過程中我們還可以對省市縣等數據建立起列表,然后動態自定義填充.
以上我們判斷屬性名是否是Name條件過嚴,我們可以適當放寬一些,則能適應的場景更廣.