[SpecFlow]在測試程式中比較單一物件與物件集合

[SpecFlow]在測試程式中比較單一物件與物件集合

前言

前一篇文章:[Unit Testing]如何驗證兩個自訂型別物件集合相等 提到了如何比較自訂的物件,以及自訂物件的集合是否相等。(偷吃步可以透過 LINQ 的 SequenceEqual() 搭配匿名型別與 Tuple 來使用)

這篇文章,則是使用 SpecFlow 的 Assist Helper 直接從 Scenario 取得的測試案例資料進行比較。

 

範例

這篇文章簡單的舉了一個例子來說明,怎麼驗證自訂型別的單一物件以及集合。 Feature 檔如下:

Feature: 射手預備

Scenario: 第一位射手是誰
	Given 射手群清單為
	| Name | Id |
	| Joey | 1  |
	| Bill | 2  |
	| Demo | 3  |
	When I 取得第一位射手
	#錯誤的測試案例,預期第一筆應該為Joey才對
	Then 第一位射手應為 
	| Name | Id |
	| Bill | 2  |
	
Scenario: 下一批射手預備
	Given 射手群清單為
	| Name | Id |
	| Joey | 1  |
	| Bill | 2  |
	| Demo | 3  |
	When I 呼叫下一波射手預備,一次 2 位
	#錯誤的測試案例,預期第一筆應該為{Joey,1} 第二筆應為 {Bill,2}才對
	Then 射手清單為
	| Name | Id |
	| Joey | 2  |
	| Bill | 3  |

兩個測試案例可以看到刻意放了錯誤的 Then ,這裡是為了讓讀者們看到,當驗證失敗時,錯誤訊息的呈現方式。

接著來看重點, Steps 該怎麼設計。 Steps 程式碼如下:

using System.Collections.Generic;
using System.Linq;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;

namespace SpecflowAssertSample
{
    [Binding]
    [Scope(Feature = "射手預備")]
    public class 射手預備Steps
    {
        private Target target;

        [BeforeScenario]
        public void BeforeScenario()
        {
            target = null;
        }

        [Given(@"射手群清單為")]
        public void Given射手群清單為(Table table)
        {
            var shooters = table.CreateSet<Person>().ToList();
            this.target = new Target(shooters);
        }

        [When(@"I 呼叫下一波射手預備,一次 (.*) 位")]
        public void WhenI呼叫下一波射手預備一次N位(int count)
        {
            List<Person> actual = target.GetNext(count);
            ScenarioContext.Current.Set<List<Person>>(actual);
        }

        [Then(@"射手清單為")]
        public void Then射手清單為(Table table)
        {
            //var expected = table.CreateSet<Person>().ToList();
            var actual = ScenarioContext.Current.Get<List<Person>>();
            table.CompareToSet<Person>(actual);
        }

        [When(@"I 取得第一位射手")]
        public void WhenI取得第一位射手()
        {
            Person actual = target.GetFirstShooter();
            ScenarioContext.Current.Set<Person>(actual, "first");
        }

        [Then(@"第一位射手應為")]
        public void Then第一位射手應為(Table table)
        {
            var actual = ScenarioContext.Current.Get<Person>("first");
            //var expected = table.CreateInstance<Person>();
            table.CompareToInstance<Person>(actual);
        }
    }

    public class Person
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

    public class Target
    {
        private List<Person> shooters;

        public Target(List<Person> shooters)
        {
            this.shooters = shooters;
        }

        internal List<Person> GetNext(int count)
        {
            return this.shooters.Take(count).ToList();
        }

        internal Person GetFirstShooter()
        {
            return this.shooters.First();
        }
    }
}

重點說明:

  1. 要使用 SpecFlow 的 Assist Helper,記得引用 TechTalk.SpecFlow.Assist 的命名空間
  2. table.CreateSet<T> table.CreateInstance<T> 的使用方式,請參考:[BDD][Tool][SpecFlow]Scenario 上使用 table 來呈現集合
  3. 重點在 Then 的部分,當要比較單一物件時,可以直接使用 table.CompareToInstance<T>(instance) 即可。
  4. 當要比較物件集合時,可以直接使用 table.CompareToSet<T>(set) 即可。

 

驗證失敗的訊息

第一個測試案例,因為是 Scenario 就寫錯了,所以 Expected 是 Scenario 上的。

image

第二個測試案例錯誤訊息如下:

image

可以看到驗證失敗訊息的呈現,要比自己用 MSTest 等 Assert 來得清楚許多, SpecFlow 會把 property 的名稱與值都顯示出來。

 

結論

有用 SpecFlow 的 table 來呈現物件時,除了需求與測試案例說明更加清楚以外,不管是單一物件或集合,在 Steps 中要進行取值或驗證,都是一塊小蛋糕。比起自己寫或其他 Test Framework 內建的 Assert ,要友善跟好用許多,相信這個小技巧,一定可以讓大家寫測試程式更加輕鬆簡便!

 

Reference

  1. SpecFlow Assist Helpers 

對敏捷開發有興趣的朋友,可以參考我的粉絲專頁:91敏捷開發之路

對 TDD 課程有興趣的朋友,課程內容、大綱與學員心得,可以參考 skilltree 的公開課程:自動測試與 TDD 實務開發

若需要聯絡我,可以透過粉絲專頁私訊或是側欄的關於我。