引言
上一章我們介紹了在
xUnit
單元測試中用
xUnit.DependencyInject
來使用依賴註入,上一章我們的
Sample.Repository
倉儲層有一個批次註入的介面沒有做單元測試,今天用這個範例來演示一下如何用
Bogus
建立模擬數據 ,和
EFCore
的種子數據生成
Bogus 的優勢
豐富的數據生成支持 :Bogus 提供了廣泛的 API 支持,涵蓋了各種數據型別和用例,使得生成虛假數據變得非常靈活和方便。
重復性和可控性 :透過設定種子值,可以確保生成的虛假數據是可重復的,這對於需要一致的測試數據或範例數據非常有用。
易於使用 :Bogus 使用流暢的語法和簡單的方法呼叫,使得生成虛假數據變得簡單直觀,即使是對庫不熟悉的使用者也可以快速上手。
內建規則和語意 :內建了許多常見數據類別的規則和語意,例如公司名稱、產品名稱、地址等,可以快速生成符合實際場景的數據。
靈活性 :除了內建規則外,還可以透過自訂規則來生成特定的數據,滿足不同場景下的需求。
社群支持 :Bogus 是一個受歡迎的開源庫,擁有活躍的社群支持和維護,可以獲得持續的更新和改進。
Bogus 實戰
簡介
Bogus
是一個簡單的
.NET
語言(如
C#
、
F#
和
VB.NET
)的假數據生成器。
Bogus
本質上是
faker.js
的
C#
移植版本,並受到
FluentValidation
的語法糖的啟發。
使用
建立新的
xUnit
測試計畫
dotNetParadise.Bogus
Nuget
包安裝
Bogus
Install-Package Bogus
建立一個`Bogus`幫助類
```c#
using Bogus;
using Sample.Repository.Models;
namespace dotNetParadise.Bogus
{
public class BogusHelper
```package manager
PM> NuGet\Install-Package Bogus -Version 35.5.0
和上一篇的配置一樣,測試計畫需要添加倉儲層的計畫參照,並透過
Nuget
安裝
xUnit.DependencyInject
,配置
Startup
。
先看一下我們的
Staff
實體
public classStaff
{
publicint Id { get; set; }
publicstring Name { get; set; }
publicstring Email { get; set; }
publicint? Age { get; set; }
public List<string>? Addresses { get; set; }
public DateTimeOffset? Created { get; set; }
}
接下來對我們批次新增的介面進行單元測試,測試數據透過
Bogus
生成,先看使用在講解用法。
生成
500
條測試數據保存到 DB
[Fact]
publicasync Task BatchAddStaffAsync_WhenCalled_ShouldAddStaffToDatabase()
{
// Arrange
var staffs = new Faker<Staff>()
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.Age, f => f.Random.Number(18, 60))
.RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()))
.RuleFor(u => u.Created, f => f.Date.PastOffset())
.Generate(500);
// Act
await _staffRepository.BatchAddStaffAsync(staffs, CancellationToken.None);
// Assert
var retrievedStaffs = await _staffRepository.GetAllStaffAsync(CancellationToken.None);
Assert.NotNull(retrievedStaffs); // 確保 Staff 已成功添加到資料庫
Assert.Equal(500, retrievedStaffs.Count); // 確保正確數量的 Staff 已添加到資料庫
Assert.True(staffs.All(x => retrievedStaffs.Any(_ => x.Id == _.Id)));
}
看程式碼配置跟
FluentValidation
都是一樣都是透過
RuleFor
來配置實體的內容
看一下生成的測試數據
Run Tests
單元測試成功,有了
Bogus
之後我們建立一些測試數據就方便多了
Bogus 的用法
locales 國際化
Bogus
支持許多不同的地區設定(
locales
),這些地區設定可用於生成特定語言或地區的虛假數據。您可以透過設定不同的
locale
參數來使用不同的地區設定。
Bogus
支持以下地區設定(
locales
)
Locale Code | Language | Locale Code | Language |
---|---|---|---|
af_ZA | Afrikaans | fr_CH | French (Switzerland) |
ar | Arabic | ge | Georgian |
az | Azerbaijani | hr | Hrvatski |
cz | Czech | id_ID | Indonesia |
de | German | it | Italian |
de_AT | German (Austria) | ja | Japanese |
de_CH | German (Switzerland) | ko | Korean |
el | Greek | lv | Latvian |
en | English | nb_NO | Norwegian |
en_AU | English (Australia) | ne | Nepalese |
en_AU_ocker | English (Australia Ocker) | nl | Dutch |
en_BORK | English (Bork) | nl_BE | Dutch (Belgium) |
en_CA | English (Canada) | pl | Polish |
en_GB | English (Great Britain) | pt_BR | Portuguese (Brazil) |
en_IE | English (Ireland) | pt_PT | Portuguese (Portugal) |
en_IND | English (India) | ro | Romanian |
en_NG | Nigeria (English) | ru | Russian |
en_US | English (United States) | sk | Slovakian |
en_ZA | English (South Africa) | sv | Swedish |
es | Spanish | tr | Turkish |
es_MX | Spanish (Mexico) | uk | Ukrainian |
fa | Farsi | vi | Vietnamese |
fi | Finnish | zh_CN | Chinese |
fr | French | zh_TW | Chinese (Taiwan) |
fr_CA | French (Canada) | zu_ZA | Zulu (South Africa) |
有些地區設定可能沒有完整的數據集,比如說,有些語言可能缺少某些數據集,例如中文(
zh_CN
)可能沒有
lorem
數據集,但韓語(
ko
)有。在這種情況下,
Bogus
會預設使用英文(
en
)的數據集。換句話說,如果找不到特定語言的數據集,就會退而使用英文的數據集。如果您有興趣幫助貢獻新的地區設定或更新現有的設定,請檢視我們的建立地區設定頁面獲取更多資訊。
來驗證一下
[Theory]
[InlineData(null)]
[InlineData("zh_CN")]
publicvoidLocales_ConfigTest(string? locale)
{
//default
var faker = locale isnull ? new Faker<Staff>() : new Faker<Staff>(locale);
faker.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.Age, f => f.Random.Number(18, 60))
.RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
.RuleFor(u => u.Created, f => f.Date.PastOffset());
var staff = faker.Generate();
var consoleType = locale isnull ? "default" : locale;
testOutputHelperAccessor.Output?.WriteLine($"{consoleType}:{JsonConvert.SerializeObject(staff)}");
}
OutPut
default:{"Id":0,"Name":"Clyde Price","Email":"[email protected]","Age":39,"Addresses":["46277 Abraham Parkways, South Spencerland, Guadeloupe","6470 Porter Island, Lesliehaven, Chad","10804 Halvorson Brook, Ninaton, Iran"],"Created":"2023-04-30T11:31:35.5106219+08:00"}
zh_CN:{"Id":0,"Name":"昊焱 尹","Email":"[email protected]","Age":58,"Addresses":["孫橋5號, 珠林市, Costa Rica"],"Created":"2024-02-11T08:16:49.1807504+08:00"}
可以看出預設是
en
英文,透過設定
locale
可以實作國際化的輸出。
生成相同數據集
// 如果您希望生成可重復的數據集,請設定隨機數種子。
Randomizer.Seed = new Random(8675309);
這段程式碼用於設定隨機數生成器的種子,以便生成可重復的數據集。透過指定一個固定的種子值,可以確保每次執行生成的隨機數據都是相同的,從而實作數據集的重復性。
這個比較有意思,我們來做個
demo
,要求隨機生成五個物件 要求下一次執行生成的還是同一批物件。
用
Bogus
的
Seed
就很容易實作。
[Fact]
publicvoidBogus_Compare_SeedTest()
{
// Arrange
var faker = new Faker<Staff>()
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.Age, f => f.Random.Number(18, 60))
.RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
.RuleFor(u => u.Created, f => f.Date.PastOffset());
// Act
var staffs1 = Enumerable.Range(1, 5)
.Select(_ => faker.UseSeed(_).Generate())
.ToList();
OutputStaffInformation(staffs1, "第一次");
var staffs2 = Enumerable.Range(1, 5)
.Select(_ => faker.UseSeed(_).Generate())
.ToList();
OutputStaffInformation(staffs2, "第二次");
// Assert
Assert.True(staffs1.All(staff1 => staffs2.Any(staff2 => staff1.Name == staff2.Name && staff1.Email == staff2.Email)));
}
privatevoidOutputStaffInformation(List<Staff> staffs, string iteration)
{
foreach (Staff staff in staffs)
{
testOutputHelperAccessor.Output?.WriteLine($"{iteration}: name: {staff.Name}, email: {staff.Email}");
}
}
Arrange
部份初始化了一個
Faker<Staff>
例項,並定義了一系列規則來生成
Staff
物件。
Act
部份透過使用不同的種子值,生成了兩組包含 5 個
Staff
物件的列表,並輸出了每個
Staff
物件的姓名和信箱資訊。
Assert
部份使用斷言驗證了兩組生成的
Staff
列表中是否存在具有相同姓名和信箱的物件,即透過 A
ll 和 Any
方法進行比較。
透過使用不同的種子值來生成多組數據,然後斷言這些數據中是否存在相同的姓名和信箱資訊。
Bogus Api 支持
Bogus
之所以提供這麽方便的假數據生成,得益於封裝了開箱即用的獲取各類數據的方法,如:
Address
ZipCode - 獲取郵政編碼。
City - 獲取城市名稱。
StreetAddress - 獲取街道地址。
CityPrefix - 獲取城市字首。
CitySuffix - 獲取城市字尾。
StreetName - 獲取街道名稱。
BuildingNumber - 獲取建築編號。
StreetSuffix - 獲取街道字尾。
SecondaryAddress - 獲取次要地址,如 '公寓 2' 或 '321 號套房'。
County - 獲取縣名。
Country - 獲取國家。
FullAddress - 獲取完整地址,包括街道、城市、國家。
CountryCode - 獲取隨機的 ISO 3166-1 國家程式碼。
State - 獲取隨機州名。
StateAbbr - 獲取州名縮寫。
Latitude - 獲取緯度。
Longitude - 獲取經度。
Direction - 生成基數或序數方向,例如:西北、南、西南、東。
CardinalDirection - 生成基數方向,例如:北、南、東、西。
OrdinalDirection - 生成序數方向,例如:西北、東南、西南、東北。
Commerce
Department - 獲取隨機商務部門。
Price - 獲取隨機產品價格。
Categories - 獲取隨機產品類別。
ProductName - 獲取隨機產品名稱。
Color - 獲取隨機顏色。
Product - 獲取隨機產品。
ProductAdjective - 隨機產品形容詞。
ProductMaterial - 隨機產品材料。
Ean8 - 獲取隨機的 EAN-8 條形碼號碼。
Ean13 - 獲取隨機的 EAN-13 條形碼號碼。
後面的可以檢視官網
Api
官網地址在文末...
Bogus 庫提供了豐富的 API 支持,涵蓋了各種數據型別和用例,包括地址、商務、日期、金融、圖片、互聯網、Lorem 文本、姓名、電話等方面的虛假數據生成方法。
EFCore 利用 Bogus 生成種子數據
在我們的
Sample.Repository
中設定種子數據
使用
Bogus
庫生成虛假數據,填充到 Staffs 列表
public classFakeData
{
publicstatic List<Staff> Staffs = [];
publicstaticvoidInit(int count)
{
var id = 1;
var faker = new Faker<Staff>()
.RuleFor(_ => _.Id, f => id++)
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Person.Email)
.RuleFor(u => u.Age, f => f.Random.Number(18, 60))
.RuleFor(u => u.Addresses, f => f.MakeLazy(f.Random.Number(1, 3), () => f.Address.FullAddress()).ToList())
.RuleFor(u => u.Created, f => f.Date.PastOffset());
var staffs = faker.Generate(count);
FakeData.Staffs.AddRange(staffs);
}
}
Program
寫入 1000 條種子數據
using (var context = app.Services.CreateScope().ServiceProvider.GetRequiredService<SampleDbContext>())
{
context.Database.EnsureCreated();
FakeData.Init(1000);
await context.Staffs.AddRangeAsync(FakeData.Staffs);
await context.SaveChangesAsync();
}
我這地方用的是
Microsoft.EntityFrameworkCore.InMemory
記憶體資料庫,正常如果使用像
Sqlserver
,
MySQL
等
CodeFirst
模式可以在 DbContext 的
OnModelCreating
配置種子數據。
protectedoverridevoidOnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
//FakeData.Init(1000);
//builder.Entity<Staff>().HasData(FakeData.Staffs);
}
來測試一下
透過我們
Sample.Api
提供的
GetAll
的方法測試一下種子數據
正好一千條測試數據,大功告成。
最後
在軟體開發中,使用
Bogus
可以極大地簡化測試數據的建立過程,同時結合
EFCore
的種子數據功能,可以快速生成並初始化資料庫中的虛假數據。這種方法不僅提高了開發效率,還能確保測試數據的品質和一致性。透過本文的範例和說明,希望您能更加熟悉如何利用
Bogus
和
EFCore
來生成模擬數據和種子數據,從而為軟體開發過程提供更好的支持和幫助,我們有大量數據的測試需求時,也不用再為創造數據而煩惱。
Bogus Github [1]
本文完整原始碼 [2]
參考資料
[1]
Bogus Github:
https://github.com/bchavez/Bogus
本文完整原始碼:
https://github.com/Dong-Ruipeng/dotNetParadise-xUnit