首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

SpecFlow 實戰:用行為驅動開發 (BDD) 驅動 .NET 專案品質

     編輯:關於.NET

身為資料存取領域的開發者,我長期習慣從資料庫向上建構應用程式。然而,隨著架構師兼測試人員 Dennis Doire 在我主持的用戶群中分享 SpecFlow 與 Selenium 進行行為驅動開發 (BDD) 的實戰經驗,我對這種「由外而內」的開發方式產生了濃厚興趣。BDD 並非單純的測試技術,而是一種強調業務場景與驗收條件的協作方法論,它彌補了傳統單元測試只關注單一物件行為、卻忽略使用者真實操作流程的缺口。

BDD 核心:從使用者情境出發

BDD 是測試驅動開發 (TDD) 的演進形式,核心在於描述「使用者在特定情境下的預期行為」。不同於傳統單元測試驗證方法回傳值,BDD 關注完整的業務流程。例如:「當使用者填妥『新增客戶』表單並點擊『儲存』時,系統應持久化資料並回饋成功訊息」。這類描述直接對應驗收條件,讓非技術利害關係人也能參與討論。

SpecFlow 是 .NET 生態系中實踐 BDD 的利器,它整合 Visual Studio,採用 Gherkin 語法(Given/When/Then)撰寫可讀性極高的功能規格文件,並自動產生對應的測試步驟程式碼。這讓「活文檔」成為可能:規格即測試,測試即規格。

實作演練:建立第一個 SpecFlow 專案

  1. 在 Visual Studio 中安裝 SpecFlow 擴充套件(透過「擴充功能和更新」或 NuGet)。
  2. 建立「單元測試專案」作為測試入口,參考 TechTalk.SpecFlow.dll。
  3. 新增 Features 資料夾,並在其下建立子資料夾 Add 存放新增客戶相關規格。
  4. 加入 SpecFlow Feature File,命名為 AddCustomer.feature。

編寫第一個功能規格(Feature):

Feature: Add Customer
  Allow users to create and store new customers
  As long as the new customers have a first and last name

Scenario: HappyPath
  Given a user has entered information about a customer
  And she has provided a first name and a last name as required
  When she completes entering more information
  Then that customer should be stored in the system

Scenario: Missing Required Data
  Given a user has entered information about a customer
  And she has not provided the first name and last name
  When she completes entering more information
  Then that user will be notified about the missing data
  And the customer will not be stored into the system

從規格到程式碼:步驟定義與綁定

右鍵點擊 .feature 檔案選擇「產生步驟定義」,SpecFlow 會根據 Gherkin 步驟自動產生對應的 C# 方法骨架。將產生的程式碼貼入新建的 Steps.cs,並標記 [Binding] 屬性。此時每個步驟方法預設呼叫 ScenarioContext.Current.Pending(),執行測試會顯示「步驟定義未實作」。

接著注入領域邏輯。為了驗證「客戶被存入系統」,我們需要 Customer 實體與 CustomerRepository:

public class Customer {
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

public class CustomerRepository {
  private readonly CustomerContext _context = new CustomerContext();
  public void Add(Customer c) => _context.Customers.Add(c);
  public int Save() => _context.SaveChanges();
  public Customer FindById(int id) => _context.Customers.Find(id);
}

public class CustomerContext : DbContext {
  public DbSet<Customer> Customers { get; set; }
}

在步驟實作中建立客戶、設定屬性、呼叫存儲庫並斷言:

[Given(@"a user has entered information about a customer")]
public void GivenUserEnteredCustomerInfo() => _customer = new Customer();

[Given(@"she has provided a first name and a last name as required")]
public void GivenProvidedRequiredNames() {
  _customer.FirstName = "Julie";
  _customer.LastName = "Lerman";
}

[When(@"she completes entering more information")]
public void WhenCompletedEntry() { /* 無額外動作 */ }

[Then(@"that customer should be stored in the system")]
public void ThenCustomerStored() {
  _repository = new CustomerRepository();
  _repository.Add(_customer);
  _repository.Save();
  // 關鍵斷言:ID 大於 0 表示確實寫入資料庫(EF 會回填身分識別值)
  Assert.IsTrue(_customer.Id > 0);
}

避開「假陽性」陷阱:理解 ORM 行為

初次執行測試通過後,我發現一個隱患:若註解掉 _repository.Save(),測試竟然仍通過!原因在於 EF Core 的 DbSet.Find 會優先檢查變更追蹤器中的實體,而非真正查詢資料庫。此時客戶 Id 仍為 0,卻被 FindById 找到,導致斷言 Assert.IsNotNull(repo.FindById(id)) 錯誤通過。

正確做法是斷言 _customer.Id > 0,確保 SaveChanges 真正執行並由資料庫產生識別值。這也提醒我們:BDD 測試雖以業務語言描述,但實作細節必須考量底層框架特性,必要時補充整合測試以驗證持久化行為。

BDD 與開發者的角色

與 Fluent Assertions 作者 Dennis Doomen 交流後確認:開發者應將技術知識(如 ORM 行為、交易邊界)融入步驟實作,但不可將技術術語強加給業務專家。雙方共同建構通用語言,才能讓 BDD 發揮最大價值。

延伸資源

下載完整程式碼範例,立即體驗 SpecFlow 驅動的 BDD 開發流程。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved