iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Software Development

單元測試從入門到進階之路 (以 C# NUnit 3 X NSubstitute 為例)系列 第 24

Day 24-Unit Test 應用於 ORM (以 Entity Framework 為例) (情境及應用-4)

Unit Test 應用於 ORM (以 Entity Framework 為例) - LINQ 介紹

今天的主題是要針對開發 .NET 應用程式最常遇到的資料庫處理(參考文章:Easily Perform LINQ Mocking to Unit Test ASP.NET Core Application),而資料庫處理手法有數種方式如原生 SQL、Entity Framework、Dapper 等手法;其中,我們以 Entity Framework 為本次的範例,再討論 Entity Framework 之前,要先了解 C# 很常用的資料處理語法 —— LINQ。

LINQ 是一套可做為 .NET 開發環境中 C# 資料物件的查詢、傳遞、轉換的語法框架,也就是在 .NET 環境中可以用 SQL 的邏輯對資料進行處理;舉例來說,若沒有 LINQ 語法之前,假設現在有一組 C# 物件要取出符合條件的資料,程式碼如下:

class Program
{
    static void Main()
    {
       List<string> classmates = new List<string> { "David", "Mary", "Mandy", "Kevin" };
       List<string> classmatesMStart = new List<string>();
      
       foreach (string classmate in classmates)
       {
         if (classmate[0].Equals("M"))
         {
           classmatesMStart.Add(classmate);
         }
       }
       
       foreach (string classmate in classmates)
       {
         Console.WriteLine(classmate);
       }
    }
}

但如果今天是用 LINQ 語法,則程式碼如下:

class Program
{
    static void Main()
    {
       List<string> classmates = new List<string> { "David", "Mary", "Mandy", "Kevin" };
       List<string> classmateMStart = classmates.Where(c => c[0].Equals("M")).ToList();
      
       foreach (string classmate in classmates)
       {
         Console.WriteLine(classmate);
       }
    }
}

所以實務上還滿常使用 LINQ,那接下來概述 Entity Framework 的功能。


Unit Test 應用於 ORM (以 Entity Framework 為例) - Entity Framework 介紹

Entity Framework 是應用於 .NET 與 SQL 之間溝通的橋樑,採用 (ORM, Object Relational Mapping) 技術,使用 Entity Framework 的好處是像資料庫連線、關閉等等都已經處理好,只需專注在商業邏輯的撰寫上即可。其中,使用 Entity Framework 會自動產製類似如下的程式碼,這部分主要是跟 SQL 資料表之間的聯繫;同時,也是我們單元測試主要的測試目標,若想了解更細的內容,可參考 Entity Framework Tutorial

public partial class ClassmateContext : DbContext
{
    public ClassmateContext()
    {
    }

    public ClassmateContext(DbContextOptions<ClassmateContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Classmate> Classmates { get; set; }
    
    // ...
}

https://ithelp.ithome.com.tw/upload/images/20210924/20127378Sqx1fkifXA.png
圖一、Entity Framework 框架圖(圖源:Entity Framework Tutorial)


Unit Test 應用於 ORM (以 Entity Framework 為例) - 用程式碼說故事

因此,今天的主題就在於如何在使用 Entity Framework 的情境下,做測試時把 Entity Framework 相關的元件抽離並注入假物件,並且假物件需符合 LINQ 可使用的格式,那我們先來看商業邏輯的部分:

public class ClassmateClass {
    private ClassmateContext ct;
    
    public ClassmateClass(ClassmateContext inCt) {
        ct = inCt;
    }

    public int GetClassmateId(string classmateName)
    {
         int classmateId = 0;

         if (!string.IsNullOrEmpty(classmateName))
         {
           classmateId = ct.Classmates
                           .Where(d => d.Name.Equals(classmateName))
                           .Select(d => d.Id)
                           .FirstOrDefault();
         }

         return classmateId;
    }
}

在從商業邏輯中,我們可以看到 Id 主要是從 Context 中的 Classmate Dbset 取得相對應的資料;因此,接下來要做的事情就是把 ClassmateContext 抽換成虛設常式,程式碼如下:

public class StubClassmateContext<T>
{
   public ClassmateContext GetClassmateContext()
   {
        var options = new DbContextOptionsBuilder<ClassmateContext>()
                       .UseInMemoryDatabase(Guid.NewGuid().ToString())
                       .Options;
                         
        var context = new ClassmateContext(options);

        context.Classmates.Add(new Classmate { Id = 100, Name = "Kevin" });
        context.SaveChanges();
        
        return context;
    }
}

因此,就可以透過建構函式把虛設常式注入,如下:

[Test]
public void DemoStubContextTest()
{
    // Arrange
    var stubCt = new StubClassmateContext<ClassmateContext>().GetClassmateContext();
    var classmateClass = new ClassmateClass(stubCt);
    
    // Act
    int classmateId = classmateClass.GetClassmateId("Kevin");

    // Assert
    Assert.AreEqual(100, classmateId);
}

這樣就算是把 Entity Framework 連線至真實的資料庫抽換成自己設定的假資料,雖然在建立 StubClassmateContext 時所建立的連線依舊有用 Entity Framework 的語法(換言之,已經並非達到百分百自己控制的測試碼);但以能驗證從 Entity Framework 取出來的資料所做的邏輯是否正確,還是有其效益。


上一篇
Day 23-Unit Test 應用於 DateTime-2 (情境及應用-3)
下一篇
Day 25-Unit Test 應用於 Async Code-1 (情境及應用-5)
系列文
單元測試從入門到進階之路 (以 C# NUnit 3 X NSubstitute 為例)30

尚未有邦友留言

立即登入留言