1. 程式人生 > >.net測試篇之測試神器Autofixture基本配置一

.net測試篇之測試神器Autofixture基本配置一

系列目錄

實際工作中我們需要的資料邏輯萬千,千變萬化,而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條件過嚴,我們可以適當放寬一些,則能適應的場景更廣.