[C#.NET][Unit Test] 採用 LocalDB 進行集成測試

集成測試主要是測試個元件之間的互動是否如預期,在這個階段的測試,我會把程式進入點 UI Layer 換成單元測試專案,由測試專案取代之,為什麼不是直接從UI測,原因很簡單,因為 UI 的變化太快了,一方面為了減少因 UI 改變而衍生出額外的工作,另一方面則為了提高測試程式碼的重用性,所以我會從 BLL 測試

三層式架構,物件彼此之間的關係,如下圖:

一般而言,集成測試在單元測試之後;當你了解完成需求功能遠比任何事都重要,就不會拘限誰先誰後。

 

單元測試成為程式進入點,如下圖:

 

當測試會需要用到資料庫,就必須要讓 CI Server 也能順利的存取到資料庫,以便運行自動化測試,對資料庫的要求,

我需要:
  • 測試資料庫已存在專案所需資料。
  • 確保每次運行測試時,不會有人弄髒測試資料庫。
  • 任何人拿從版控上拿到專案,不需要太多的設定就能運行任何測試。
  • CI Server 也能運行測試。
我採用:

LocalDB,

原因一:VS 安裝時會一併安裝 LocalDB,開發人員不需要另外準備環境

  • VS 2013 預設安裝 SQL Server 2012 Express LocalDB,預設執行個體名稱(localdb)\v11.0
  • VS 2015 預設安裝 SQL Server 201 Express LocalDB,預設執行個體名稱(localdb)\MSSQLLocalDB
若團隊內的開發環境尚未統一,要確保每一個開發環境的執行個體名稱一樣

原因二:mdf 附加檔案很輕鬆

開發環境:

接著來看看我的專案分層結構,如下圖:

左邊的 Solution Explorer 是各個 Layer 的專案

右邊的Code Map可以看出專案彼此之間的相依關係,虛線代表專案參考,紅線代表呼叫

 

TestDB 專案:
  • 提供給所有的測試專案使用
  • 裝載著 mdf 檔案,這個檔案怎麼來?有兩種方式
  1. 從資料庫目錄下複製 *.mdf
  2. 由專案新增 *.mdf
  • *.mdf 檔案設為 Copy if newer
IntegrateUnitTestProject 專案:

運行測試

[TestMethod]
public void BLL_TestMethod()
{
    var expected = 2;
    FlowBLL bll = new FlowBLL();
    var members = bll.GetMembers();
    var actual = members.Count();
    Assert.AreEqual(expected, actual);
}

 

專案範本如下:

https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.UnitTestAndLocalDB/


從資料庫目錄下複製 *.mdf

1.找出資料庫相對應的 mdf 檔案

 

2.讓資料庫離線


 

3.複製 *.mdf 到 VS 專案


由專案新增 *.mdf


LocalDB 連線字串

指定資料庫名稱 Initial Catalog=TestDB ,這樣才不會在SSMS看到一堆沒有用的資料庫連線

<add name="DAL"
    connectionString="Data Source=(LocalDB)\MSSQLLocalDB;
                      AttachDbFilename=|DataDirectory|\TestDB.mdf;
                      Initial Catalog=TestDB;
                      MultipleActiveResultSets=True;
                      Integrated Security=True;
                      App=EntityFramework"
    providerName="System.Data.SqlClient" />

設定 DeploymentItem

第一個參數:因為 TestDB 專案的 TestDB.mdf,放在 "App_Data" 目錄,輸出到 IntegrateUnitTestProject/bin 也仍然會維持相同結構,所以這裡要設定 "App_Data\\TestDB.mdf"

第二個參數:我想要讓 TestDB.mdf 存放在測試結果目錄的 "App_Data",所以這裡設定 "App_Data"

[TestClass]
[DeploymentItem("App_Data\\TestDB.mdf", "App_Data")]
public class UnitTes1
{
  //...
}

測試專案的輸出目錄,他在方案目錄下的TestResults,每次運行測試,他會依時間產生目錄,目錄底下有 in/out 資料夾,如下圖:

DeploymentItem 會把 *.mdf 複製一份到 out 資料夾,也就是說在測試專案裡處理的資料不會弄髒 TestDB 專案的 TestDB.mdf,如下圖:


設定 ClassInitialize

測試類別初始化,這裡要準備兩個動作

這是一個很神奇的寫法,缺了不會跑,如下代碼:

var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;

這裡是在設定 DataDirectory 所對應到的 TestResults/out 目錄,DataDirectory 指的是連線字串裡的 |DataDirectory|

AppDomain.CurrentDomain.SetData(
    "DataDirectory",
    Path.Combine(context.TestDeploymentDir, "App_Data"));

 

完整程式碼:

[ClassInitialize]
public static void SetUp(TestContext context)
{
	var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;

	AppDomain.CurrentDomain.SetData(
		"DataDirectory",
		Path.Combine(context.TestDeploymentDir, "App_Data"));
}

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo