1. 程式人生 > >使用xUnit為.net core程式進行單元測試(4)

使用xUnit為.net core程式進行單元測試(4)

資料驅動的測試

開啟PlayerCharacterShould.cs

新增幾個Fact測試方法:

        [Fact]
        public void TakeZeroDamage()
        {
            _sut.TakeDamage(0);
            Assert.Equal(100, _sut.Health);
        }

        [Fact]
        public void TakeSmallDamage()
        {
            _sut.TakeDamage(
1); Assert.Equal(99, _sut.Health); } [Fact] public void TakeMediumDamage() { _sut.TakeDamage(50); Assert.Equal(50, _sut.Health); } [Fact] public void TakeMinimum1Damage() { _sut.TakeDamage(
101); Assert.Equal(1, _sut.Health); }

Build, Run tests. 都Pass了.

仔細看下這4個方法, 他們其實是做了同樣的事情, 只不過輸入的資料和期待的結果不同而已. 

所以我們應該重構一下這段程式碼.

Theory:

針對上述情況, 我們就不再使用Fact屬性標籤了, 而是需要使用Theory.

Theory標籤會告訴xUnit, 它下面的測試方法會被執行多次, 而每次執行必須為這個方法提供必要的測試資料. 

如何為其新增測試資料呢? 首先要為測試方法新增引數, 使用引數來代替具體的數值:

        [Theory]
        public void TakeDamage(int damage, int expectedHealth)
        {
            _sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, _sut.Health);
        }

然後我們需要告訴xUnit這個測試方法的引數來自哪裡.

1. 最簡單的辦法是使用InlineData屬性標籤:

        [Theory]
        [InlineData(0, 100)]
        [InlineData(1, 99)]
        [InlineData(50, 50)]
        [InlineData(101, 1)]
        public void TakeDamage(int damage, int expectedHealth)
        {
            _sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, _sut.Health);
        }

上面我添加了四組測試資料, 每對資料按順序對應測試方法的兩個引數. (InlineData的引數型別是params object[])

然後Build, 檢視Test Explorer:

會發現這裡面多出來了4個測試, 分別對應那4個InlineData.

Run Tests, 都會Pass的.

現在就可以把那四個Fact測試方法刪除了.

儘管InlineData使用起來還是很方便, 但是在某些情境下還是靈活性欠佳, 請您檢視NonPlayerCharacterShould.cs裡面的程式碼. 取消裡面的註釋:

namespace Game.Tests
{
    public class NonPlayerCharacterShould
    {
        [Theory]
        [InlineData(0, 100)]
        [InlineData(1, 99)]
        [InlineData(50, 50)]
        [InlineData(101, 1)]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();

            sut.TakeDamage(damage);

            Assert.Equal(expectedHealth, sut.Health);
        }
    }
}

首先Build, Run Tests, 都Pass.

這個Theory的四組引數和上面的是一樣的.

2.為了共享這幾組測試資料, 可以使用MemberData屬性標籤, 首先建立一個類InternalHealthDamageTestData.cs:

namespace Game.Tests
{
    public class InternalHealthDamageTestData
    {
        private static readonly List<object[]> Data = new List<object[]>
        {
            new object[] {0, 100},
            new object[] {1, 99},
            new object[] {50, 50},
            new object[] {101, 1}
        };

        public static IEnumerable<object[]> TestData => Data;
    }
}

這裡面的資料和之前的那四組資料是一樣的.

然後修改NonPlayerCharacterShould裡面的程式碼, 把InlineData都去掉:

namespace Game.Tests
{
    public class NonPlayerCharacterShould
    {
        [Theory]
        [MemberData(nameof(InternalHealthDamageTestData.TestData), MemberType = typeof(InternalHealthDamageTestData))]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();

            sut.TakeDamage(damage);

            Assert.Equal(expectedHealth, sut.Health);
        }
    }
}

這裡改成了MemberData, 它的引數很多, 第一個引數是資料提供類的屬性名字, 這個屬性型別要求是IEnumberable的, 所以這裡應該寫"TestData", 不過最好還是使用nameof, 這樣如果更改了資料類的屬性名稱, 那麼編譯時就會報錯, 而不會導致測試失敗.

然後還需要設定MemberType屬性, 表明資料提供類的型別.

Clean Solution, Build, 可以看到還是有4個測試, Run Tests, 都會Pass的.

針對PlayerCharacterShould, 也這樣修改. 這樣測試資料就得到了共享.

3. 外部資料.

檢視一下專案裡面的TestData.csv: 裡面還是這四組資料:

0, 100
1, 99
50, 50
101, 1

再建立一個類ExternalHealthDamageTestData.cs來取出csv中的資料:

namespace Game.Tests
{
    public class ExternalHealthDamageTestData
    {
        public static IEnumerable<object[]> TestData
        {
            get
            {
                string[] csvLines = File.ReadAllLines("TestData.csv");
                var testCases = new List<object[]>();
                foreach (var csvLine in csvLines)
                {
                    IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
                    object[] testCase = values.Cast<object>().ToArray();
                    testCases.Add(testCase);
                }
                return testCases;
            }
        }
    }
}

修改一下NonPlayerCharacterShould和PlayerCharacterShould相關測試方法的屬性標籤:

namespace Game.Tests
{
    public class NonPlayerCharacterShould
    {
        [Theory]
        [MemberData(nameof(ExternalHealthDamageTestData.TestData), MemberType = typeof(ExternalHealthDamageTestData))]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();

            sut.TakeDamage(damage);

            Assert.Equal(expectedHealth, sut.Health);
        }
    }
}
        [Theory]
        [MemberData(nameof(ExternalHealthDamageTestData.TestData), MemberType = typeof(ExternalHealthDamageTestData))]
        public void TakeDamage(int damage, int expectedHealth)
        {
            _sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, _sut.Health);
        }

Build, 檢視Test Explorer:

針對他們中的任意一個類, 只能發現一個相關的測試, 而不是四個測試.

Run Tests的話, 會報錯:

它找不到TestData.csv, 這是因為我們需要更改一下csv檔案的屬性, 把它改成Copy always:

然後選擇Rebuild Solution, 這樣才能保證csv檔案被copy到正確的位置.

再檢視Test Explorer:

這時就會看到4組測試了, Run Tests, 都會Pass的.

如果再新增一組資料, 還是需要Rebuild Solution的, 然後新的測試會出現在Test Explorer裡面.

4.CustomDataAttribute 自定義資料屬性標籤.

使用自定義的標籤可以把測試資料在test case和class之間共享, 而且會提高測試的可讀性.

建立一個類 HealthDamageDataAttribute.cs:

namespace Game.Tests
{
    public class HealthDamageDataAttribute : DataAttribute
    {
        public override IEnumerable<object[]> GetData(MethodInfo testMethod)
        {
            yield return new object[] { 0, 100 };
            yield return new object[] { 1, 99 };
            yield return new object[] { 50, 50 };
            yield return new object[] { 101, 1 };
        }
    }
}

這裡需要實現xUnit的DataAttribute這個抽象類.

修改NonPlayerCharacterShould和PlayerCharacterShould的相關方法, 把上面的自定義標籤寫上去:

namespace Game.Tests
{
    public class NonPlayerCharacterShould
    {
        [Theory]
        [HealthDamageData]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();

            sut.TakeDamage(damage);

            Assert.Equal(expectedHealth, sut.Health);
        }
    }
}

Build, 然後再Test Explorer還是可以看到四組測試, 如果再想新增一組測試, 只需重新Build即可.

測試同樣都會Pass的.

同樣自定義標籤可以整合外部資料, 這個很簡單, 您自己來寫一下吧.

這個xUnit簡介就到此為止了, 想要深入瞭解的話, 還是看官方文件吧. 

相關推薦

使用xUnit.net core程式進行單元測試(4)

資料驅動的測試 開啟PlayerCharacterShould.cs 新增幾個Fact測試方法: [Fact] public void TakeZeroDamage() { _sut.TakeDamage(

使用xUnit.net core程式進行單元測試(3)

測試的分組 開啟Game.Tests裡面的BossEnemyShould.cs, 為HaveCorrectPower方法新增一個Trait屬性標籤: [Fact] [Trait("Category", "Enemy")] public v

使用xUnit.net core程式進行單元測試 -- Assert

Assert Assert做什麼?Assert基於程式碼的返回值、物件的最終狀態、事件是否發生等情況來評估測試的結果。Assert的結果可能是Pass或者Fail。如果所有的asserts都pass了,那麼整個測試就pass了;如果有任何assert fail了,那麼測試就fail了。 xUnit提供

使用xUnit.net core程式進行單元測試(1)

導讀 為什麼要編寫自動化測試程式(Automated Tests)? 可以頻繁的進行測試 可以在任何時間進行測試,也可以按計劃定時進行,例如:可以在半夜進行自動測試。 肯定比人工測試要快。 可以更快速的發現錯誤。 基本上是非常可靠的。 測試程式碼與生產程式碼緊密結合。 使得開發團隊更具有幸

.NET Core: 在.NET Core進行單元測試

單元測試能夠幫助開發人員確保所開發的模組、類以及類中的方法等的正確性,在專案開發過程中,及時進行單元測試能夠避免不必要的BUG以及提高測試效率。 在本文中,我們會分別來學習如何使用MSTest、xUnit以及NUnit這些流行的.NET測試框架來對.NET Core專案進行測試。 一、專案建立 首先,建

好代碼是管出來的——.Net Core中的單元測試與代碼覆蓋率

情況 其它 netcore output 窗口 一個數據庫 過濾 and 令行   測試對於軟件來說,是保證其質量的一個重要過程,而測試又分為很多種,單元測試、集成測試、系統測試、壓力測試等等,不同的測試的測試粒度和測試目標也不同,如單元測試關註每一行代碼,集成測試關註的是

Spark程式進行單元測試-使用scala

Spark 中進行一些單元測試技巧: 最近剛寫了一點Spark上的單元測試,大概整理了一些 rdd測試 spark程式一般從叢集中讀取資料然後通過rdd進行轉換,這其中涉及到叢集,每次修改bug,

轉 使用NUnit在.Net程式設計中進行單元測試

引言: 舉一個可能會發生在你身邊的事件將更能貼近實際,幸好我們現在就有一件在程式設計師看來非常普通的任務: 你今天第一天上班,你的專案經理拿給你一疊不算厚的文件,告訴你今天的任務是按照文件中的要求編寫一個.Net類,可能因為任務並不複雜,所以他看上去非常的隨意。 今天能否很好的完成任務對你來說非常特殊,你拿

.NET Core 3.0 單元測試與 Asp.Net Core 3.0 整合測試

單元測試與整合測試 測試必要性說明 相信大家在看到單元測試與整合測試這個標題時,會有很多感慨,我們無數次的在實踐中提到要做單元測試、整合測試,但是大多數專案都沒有做或者僅建了專案檔案。這裡有客觀原因,已經接近交付日期了,我們沒時間做白盒測試了。也有主觀原因,面對業務複雜的程式碼我們不知道如何入手做單元測試,不

淺談.Net Core後端單元測試

- [1. 前言](#head1) - [2. 為什麼需要單元測試](#head2) - [2.1 防止迴歸](#head3) - [2.2 減少程式碼耦合](#head4) - [3. 基本原則和規範](#head5) - [3.1 3A原則](#head6) - [3.2 儘量避免直接測試私有方法

使用xunit對asp.net core webapi進行集成測試

rtu sharp task 技術 分離 bubuko 們的 this eba 新項目我們采用前後端分離,後端采用asp.net core webapi, 如何對後端代碼進行自動化測試呢,有以下幾種方案: 1. 單元測試,目前這個方案對我們來說難度很大,拋開時間的問題,單

ASP.NET Core 對Controller進行單元測試

單元測試對我們的程式碼質量非常重要。很多同學都會對業務邏輯或者工具方法寫測試用例,但是往往忽略了對Controller層寫單元測試。我所在的公司沒見過一個對Controller寫過測試的。今天來演示下如果對Controller進行單元測試。以下內容預設您對單元測試有所瞭解,比如如何mock一個介面。在這裡多叨

使用Docker ComposeASP.NET Core程式新增MySQL資料庫

本文翻譯自: Adding MySQL to ASP.NET Core App With Docker Compose           之前的例子中我們將程式進行容器化,本文我們會把MySQL資料庫作為另一個Container,用於程式的訪問。因為現

使用Xunit進行單元測試

row 自動安裝 net 必須 清除 版本 多次 重構 nbsp 使用Xunit進行單元測試 來源 https://www.cnblogs.com/ccccc05/archive/2017/08/01/7266914.html 目前在.Net框架下的測試工具主要有

讓現有vue前端專案快速支援多語言 - 用.net core程式快速替換中文資源Key,咱不幹體力活

前言 這是我第一次發部落格,2020年立個flag以後要經常發。 最近應公司上層要求,需要將現有專案儘快支援多語言,而中文內容可以找專業人員翻譯。那麼咱們說幹就幹,首先我們專案的前端是用vue寫的spa程式且元件方面用的element ui,那麼自然而然想到用vue官方推薦的vue i18n,我快速過了下i1

NUnit.Framework在VS2015中如何進行單元測試

開放 ron 微軟 strong 擴展 分享 方案 mar 項目 微軟在VS2015中加入了自動化生成測試功能, 在需要測試的源文件的公共方法中右鍵既可以創建單元測試。 不過需要註意的是,要在公共方法中創建,否則會提示這個錯誤 如下是自動化單元測試界面,可以發

在vue-cli生成的項目中使用karma+chrome進行單元測試

使用 設計實現 測試用例 runner 服務 進行 ui界面 包含 node 用vue-cli生成項目時,如果選擇了單元測試,那麽會采用karma+mocha作為單元測試框架,默認使用的瀏覽器是PhantomJs。 Karma是一個基於Node.js的JavaScri

springMVC整合Junit4進行單元測試

main方法 pri tail println test pan ati 測試的 tco 版權聲明:本文為博主原創文章,未經博主允許不得轉載。 用Junit做單元測試的好處多多,博主領悟到了兩點。一是不用在每個類裏面都寫main方法然後去測試;二是可以得到每個方法執行

Dora.Interception,.NET Core度身打造的AOP框架:全新的版本

分享 ide 1.0 nuget hub tex 普通 inb .class Dora.Interception 1.0(Github地址:可以訪問GitHub地址:https://github.com/jiangjinnan/Dora)推出有一段時間了,最近花了點時間將它

Glib 對 C 函數進行單元測試

error ati 完成 structure 是否 pac str txt b- 1. Glib 單元測試框架 Glib 為單元測試提供了一套完整的測試框架,每個測試運行包括以下幾個部分 測試數據結構 測試 setup 與 teardown 函數 測試函數 2. 單元測