[自動測試-3] 建立測試基地

  • 406
  • 0
  • TDD
  • 2019-12-19

TDD自動測試

前言

當開發團隊日趨龐大,如果都在同一個測試資料庫上跑自動測試,難免會造成測試結果互相干擾。

為了解決這些問題,讓每個開發者都在獨立的測試環境下進行測試,Sqllocaldb會是一個很好的選擇。本篇文章分成六個步驟來完成這個範例。

  1. 建立資料庫執行個體
  2. 安裝Entity Framework
  3. 建立資料庫
  4. 建立資料表
  5. 設定Migration的Config
  6. 產生語法和更新

 

建立資料庫執行個體

這玩意其實很早就出了,安裝VS2012以上的版本,會自動安裝Sqllocaldb。如果不確定是否已經安裝,只需要打開VS開發工具叫出Package Manager Console,然後輸入指令Sqllocaldb,如下圖表示,有跑出說明就代表有安裝。

接下輸入指令sqllocaldb create "執行個體名稱",請參考下圖,會建立出Demosqllocaldb的執行個體。

執行完成之後,請打開SQL Managerment Studio,登入剛剛建好的localdb,這裡的重點只有實體名稱前方要加(localdb)。

登入後可以看到實體名稱其實是對應我們剛剛用sqllocaldb指令建出來的實體名稱,到這裡為止資料庫的執行個體已經建立完畢。Sqllocaldb參考指令

 

安裝Entity Framework

這個範例用的ORM套件是Entity Framework。而建立資料庫的方式,最常見的就是DB first和Code first,這裡採用的是Code first,如果是原本資料庫就存在的資料表,可以先用DB first的方式將layout匯入成類別,會比自己一個一個屬性手動打要來的快。但這裡不建議正式資料庫使用Code first,因為蠻危險的,localdb搞壞了,大不了就是砍掉重練,但線上資料庫搞壞了,可就不能這樣玩。

 

建立資料庫

架構圖如下,我們已經完成了建立sqllocaldb的資料庫執行個體,現在要建立資料庫。

建立資料庫第一個步驟是在WebConfig新增connectString,name的名稱可以自己定義,Entity Framework會依照這個名稱找到這條連線字串,Data Source指定成我們已經建好的執行個體名稱,Initial Catalog的名稱可自己定義,以下範例是定義成COMMON,如果(localdb)\Demolocaldb執行個體內沒有COMMON資料庫,Entity Framework會自動建立,剩下的屬性無須修改。

接著新增一個類別,繼承DbContext,名稱可以自定,這裡會用A開頭,主要是希望它排在最前面,裡面提供兩個建構子,預設就是讀取我們剛剛已經建立的COMMON連線字串。

public class APortalDbContext : DbContext
    {
        public APortalDbContext() : this("COMMON")
        {
        }

        public APortalDbContext(string nameOrConnectionString)
                : base(nameOrConnectionString)
        {
        }
    }

到這裡為止,我們已經完成了資料庫的設定。

 

建立資料表

接著要建立資料表,我們假設要新增一個Member的資料表,資料表內有兩個欄位,分別是ID和Name。其實就相當於建立一個Member的類別,裡面有兩個屬性,一個是ID,一個是Name。型別可以自己決定,

 public class Member
    {
        [Key]
        public Guid ID{ get; set; }

       [StringLength(30)]
        public string Name{ get; set; }

    }
請注意,如果你簡單宣告string,他會用nvarchar(max)當成資料庫的型別,所以建議需要在上面掛一個[StringLength(30)],來限制實際的長度。
Entity Framework要求一個資料表至少需要一個Key來識別這個實體的唯一性,所以假設類別沒有任何屬性掛上[Key]這個attribute(因為propery和attribute都翻譯成屬性,為避免混淆這裡用英文代替),在第六個步驟更新資料庫時會出現錯誤。

建立好資料表,接著我們要將它掛到我們的資料庫上,所以直接在剛剛新增的APortalDbContext的類別裡新增一個屬性,型別是DbSet,然後用泛型告知是屬於Member的型別,建議屬性名稱後面都加一個小寫的s,因為資料表裡面會有很多資料列,這是一個集合的概念。完整的程式如下所示。

public class APortalDbContext : DbContext
    {
        public APortalDbContext() : this("COMMON")
        {
        }

        public APortalDbContext(string nameOrConnectionString)
                : base(nameOrConnectionString)
        {
        }

         public DbSet<Member> Members{ get; set; }
    }

到這裡為止,就已經完成了資料表的宣告。

 

設定Migration的Config

Migration主要是在管理資料庫更新的歷程,它需要繼承DbMigrationsConfiguration,建議把AutomaticMigrationsEnabled關掉,因為更新資料庫還是需要謹慎一點比較好,當要異動資料表時,藉由下指令的方式,透過Migration幫我們生成一個檔案,這個在第六個步驟產生語法與更新會提到。程式範例如下所示。

    internal sealed class Configuration : DbMigrationsConfiguration<APortalDbContext>
    {

        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(J1A2PortalDbContext context)
        {
            var member = new Member();
            member.ID = Guid.NewGuid();
            member.Name = "Test";
            member.CodeReviewUrl = "TestUrl";

            context.Member.Add(member);
            
        }
    }

 

Seed方法是每次更新資料庫時都會執行,裡面可以做一些資料庫初始化的動作,比如新增基本檔資料,這樣一來,別人拿到你的網站專案,只要建立起sqllocaldb,網站就可以做最基本的運行。

這裡我們已經完成了簡單的設定步驟,接下來最後的動作就是要產生SQL語法並更新我們的資料庫。

 

產生語法和更新

現在萬事俱備,只欠東風了,開啟Package Manager Console,輸入add-migration "更新的名稱",如下圖所示。

接著會自動幫你生成以下檔案

這個檔案看內容應該就能理解,它裡面記載了向上更版和向下退版的資訊,之後再執行update-database,資料庫和資料表便會建立起來。更多的相關語法

預設執行update-database或add-migration等指令,如果你只有一個Migration Configuration,那它就會自動讀取該設定檔,假設你有多個,則必須明確指定參數檔的名稱。 

到此為止,已經成功建置出我們的測試基地。

_MigrationHistory資料表紀錄的是migration的每次更新的歷程記錄。
資料庫預設的路徑是放在目前的使用者資料夾下。

 

結論

整個建構的步驟老實講,有點繁瑣,但很多步驟都是一次性的工作。一旦測試基地建立完成,往後要做測試就無需再重複的建立測試資料。並且藉由sqllocalDb的隔離性,可減少測試時不避要的干擾。而本機資料庫的版本控管,可透過版本控管系統把migration產生的程式簽入版控系統中,就能確保每個成員的本機資料庫的一致性,進而享受到sqllocaldb所帶來的好處與便利。