Xunit和Nunit的區別

https://www.cnblogs.com/Leo_wl/p/5727712.htmlhtml

捨棄Nunit擁抱Xunit

 

前言

   今天與同事在討論.Net下測試框架的時候,說到NUnit等大多數測試框架的SetUp以及TearDown方法並非顯得那麼完美,因此在公司內部的項目中採用了Xunit框架。那麼到底是什麼樣的緣由,讓咱們放棄了大多數框架都在用的Nunit或MSTest框架呢?git

1. Xunit簡介

  首先奉上馬丁大叔2006年對XUnit介紹的文章,http://www.martinfowler.com/bliki/Xunit.htmlgithub

  Xunit實際上是JUnit的衍生版,最開始是應用在Smalltalk中,其目的是支持持續集成,關於單元測試等相關內容能夠參考我以前TDD系列文章,這裏不作過多的介紹,只是介紹Why we choose Xunit。sql

  GitHub地址:https://github.com/xunit/xunit編程

  官方文檔:http://xunit.github.io/緩存

 

2. Xunit簡單Demo

如此簡單:框架

 

提示:須要經過NuGet下載xunit.net和xunit.visualstudio這兩個安裝包,而後啓動「Test Explorer」運行測試,詳情請參考這裏less

 

3. Xunit對比Nunit的優勢

這部份內容參考了官方文章以及一些本身對測試框架的場景的理解,若有錯誤之處,還請指出。post

3.1 每一個測試單一實例的討論,SetUp以及TestFixtureSetUp

  請參考馬丁大師對單一實例的論述:http://martinfowler.com/bliki/JunitNewInstance.html,文章指出:對於測試緩存或每次測試以前從新實例化對象,這種作法是值得商榷的。雖然其有利於對象的調用,並且基本不用考慮對象回收的問題(僅當在TearDown中回收了資源),但這樣仍然不符合絕對意義上的「對象隔離」原則。並且有些變量是隻需全局實例化一次(在Nunit框架中要使用TestFeature建立),雖然這樣也能知足需求,可是程序中仍是有不少這種框架的特性須要熟悉,相比沒有這些框架(指沒有SetUp和TestFixtureSetUp)的語法來說跟不方便一些,固然這些僅僅是一些思考。單元測試

  同時,James Newkrik也在文章中提到,與其在SetUp中初始化更多的參數,破壞單一職責的原則,另外加上每回測試都要回顧SetUp和TearDown方法所執行的內容,倒不如將其放在Test內部,去掉SetUp和TearDown來加強測試的的表達性以及隔離性。

 

3.2 Xunit沒有ExpectException

  不採用Attribute的方式來捕捉異常有兩方面的好處:

  1. 在代碼中直接斷言(Assert)能捕捉到更多種類的異常。

  2. 遵照Arrange-Act-Assert (or "3A") 模式:即測試命名上「範圍-做用-斷言」規範。

複製代碼
public class TestClass1 
    { 
        [ Fact ] 
         public void testException() 
        { 
             Assert .Throws< InvalidOperationException >(() => operation()); 
        } 
         void operation() 
        { 
             throw new InvalidOperationException (); 
        } 
    }
複製代碼

 

3.3 Xunit更像面向切面的語言

  Xunit中使用Fact、Theory、XxxData、Fact(Timeout=n)等標籤來組織測試,從功能上講更像切面編程。 請參考下一節。

 

3.4 Xunit去除了更多的Attribute

  保留不多一部分標籤有利於簡化測試框架,加快熟悉測試框架的時間,使框架更爲簡潔、實用。

NUnit 2.2 MSTest xUnit.net Comments
[Test] [TestMethod] [Fact] Marks a test method.
[TestFixture] [TestClass] n/a xUnit.net does not require an attribute for a test class; it looks for all test methods in all public (exported) classes in the assembly.
[ExpectedException] [ExpectedException] Assert.Throws orRecord.Exception xUnit.net has done away with the ExpectedException attribute in favor of Assert.Throws. SeeNote 1.
[SetUp] [TestInitialize] Constructor We believe that use of [SetUp]is generally bad. However, you can implement a parameterless constructor as a direct replacement. See Note 2.
[TearDown] [TestCleanup] IDisposable.Dispose We believe that use of[TearDown] is generally bad. However, you can implementIDisposable.Dispose as a direct replacement. See Note 2.
[TestFixtureSetUp] [ClassInitialize] IUseFixture<T> To get per-fixture setup, implement IUseFixture<T> on your test class. See Note 3
[TestFixtureTearDown] [ClassCleanup] IUseFixture<T> To get per-fixture teardown, implement IUseFixture<T> on your test class. See Note 3
[Ignore] [Ignore] [Fact(Skip="reason")] Set the Skip parameter on the[Fact] attribute to temporarily skip a test.
n/a [Timeout] [Fact(Timeout=n)] Set the Timeout parameter on the [Fact] attribute to cause a test to fail if it takes too long to run. Note that the timeout value for xUnit.net is in milliseconds.
[Property] [TestProperty] [Trait] Set arbitrary metadata on a test
n/a [DataSource] [Theory], [XxxData] Theory (data-driven test). SeeNote 4

 

3.4 Xunit使用IDisposable和IUseFixture<T>接口來代替顯示聲明SetUp和TestFixtureSetUp

   首先,建立一個支持IDisposable對象:

複製代碼
using System;
using System.Configuration;
using System.Data.SqlClient;

public class DatabaseFixture : IDisposable
{
    SqlConnection connection;
    int fooUserID;

    public DatabaseFixture()
    {
        string connectionString = ConfigurationManager.ConnectionStrings["DatabaseFixture"].ConnectionString;
        connection = new SqlConnection(connectionString);
        connection.Open();

        string sql = @"INSERT INTO Users VALUES ('foo', 'bar'); SELECT SCOPE_IDENTITY();";

        using (SqlCommand cmd = new SqlCommand(sql, connection))
            fooUserID = Convert.ToInt32(cmd.ExecuteScalar());
    }

    public SqlConnection Connection
    {
        get { return connection; }
    }

    public int FooUserID
    {
        get { return fooUserID; }
    }

    public void Dispose()
    {
        string sql = @"DELETE FROM Users WHERE ID = @id;";

        using (SqlCommand cmd = new SqlCommand(sql, connection))
        {
            cmd.Parameters.AddWithValue("@id", fooUserID);
            cmd.ExecuteNonQuery();
        }

        connection.Close();
    }
}
複製代碼

  最後增長測試,並實現IClassFixture<DatabaseFixture>接口:

複製代碼
using System;
using System.Configuration;
using System.Data.SqlClient;
using Xunit;

public class ClassFixtureTests : IClassFixture<DatabaseFixture>
{
    DatabaseFixture database;

    public ClassFixtureTests(DatabaseFixture data)
    {
        database = data;
    }

    [Fact]
    public void ConnectionIsEstablished()
    {
        Assert.NotNull(database.Connection);
    }

    [Fact]
    public void FooUserWasInserted()
    {
        string sql = "SELECT COUNT(*) FROM Users WHERE ID = @id;";

        using (SqlCommand cmd = new SqlCommand(sql, database.Connection))
        {
            cmd.Parameters.AddWithValue("@id", database.FooUserID);

            int rowCount = Convert.ToInt32(cmd.ExecuteScalar());

            Assert.Equal(1, rowCount);
        }
    }
}
複製代碼

   從這裏讀者可能體會到,Xunit更多的利用了C#自己的一些特性,而非使用一些特殊的Attribute或者方法(例如SetUp),在設計哲學上更多的考慮了對象自動實現自我管理的機制,而非人爲去管理,從某種意義上來說,解除了部分依賴性,將部分功能交給程序C#自己處理,減小工做量。

 

4. 文章引用

Martin Flower介紹Xunit: http://www.martinfowler.com/bliki/Xunit.html

Xunit Github地址:https://github.com/xunit/xunit

Nunit 官方地址:http://www.nunit.org/

周公介紹Xunit:http://zhoufoxcn.blog.51cto.com/792419/1172320/

 

做者:Stephen Cui 
出處:http://www.cnblogs.com/cuiyansong 

版權聲明:文章屬於本人及博客園共有,凡是沒有標註[轉載]的,請在文章末尾加入個人博客地址。 

若是您以爲文章寫的還不錯,請點擊「推薦一下」,謝謝。

 
分類:  編程開發 - TDD
標籤:  XUnitTDD
相關文章
相關標籤/搜索