dotnet-testing-bogus-fake-data
npx skills add https://github.com/kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-bogus-fake-data
Agent 安装分布
Skill 文档
Bogus åè³æç¢çå¨
é©ç¨æ å¢
Bogus æ¯ä¸å .NET å¹³å°çåè³æç¢çå½å¼åº«ï¼ç§»æ¤èªèåç JavaScript å½å¼åº« faker.jsãå®å°éç¨æ¼ç¢çç實æå¼·ççåè³æï¼å¦å§åãå°åãé»è©±è碼ãé»åéµä»¶çï¼ç¹å¥é©åéè¦æ¨¡æ¬ç實ä¸çè³æçæ¸¬è©¦å ´æ¯ã
é©ç¨æ å¢
ç¶è¢«è¦æ±å·è¡ä»¥ä¸ä»»åæï¼è«ä½¿ç¨æ¤æè½ï¼
- ç¢çå ·æç實æçæ¸¬è©¦è³æï¼å§åãå°åãå ¬å¸å稱çï¼
- éè¦å¤èªè¨æå¤å°åæ ¼å¼çæ¸¬è©¦è³æ
- æ´å測試æ UI ååéè¦æ¬çè³æ
- æè½æ¸¬è©¦éè¦å¤§éçå¯¦æ ¼å¼çè³æ
- è³æåº«ç¨®åï¼Seedï¼éè¦åå§åéç¼ææ¸¬è©¦ç°å¢
æ ¸å¿å¹å¼
- ç實æè³æç¢çï¼æä¾ææç¾©çåè³æï¼å¦ç實çå§åãå°åãå ¬å¸å稱
- å¤èªè¨æ¯æ´ï¼æ¯æ´è¶
é 40 種èªè¨åå°åæ ¼å¼ï¼å
å«ç¹é«ä¸æ
zh_TWï¼ - å¯éç¾æ§ï¼éé seed æ§å¶ï¼ç¢ºä¿æ¸¬è©¦è³æçä¸è´æ§
- è±å¯çè³æé¡åï¼å §å»ºå¤ç¨® DataSetï¼æ¶µèå種ç實ä¸ççè³æé¡å
- ç°¡æ½ç Fluent APIï¼ç´è§æç¨çè¨å®èªæ³
å¥ä»¶å®è£èè¨å®
å®è£ Bogus
dotnet add package Bogus
NuGet å¥ä»¶è³è¨
| å¥ä»¶å稱 | ç¨é | NuGet é£çµ |
|---|---|---|
Bogus |
åè³æç¢çå½å¼åº« | nuget.org |
GitHub å²å庫ï¼bchavez/Bogus
æ ¸å¿æ¦å¿µ
åºæ¬èªæ³çµæ§
Bogus çæ ¸å¿æ¯ Faker<T> é¡å¥ï¼ä½¿ç¨ RuleFor æ¹æ³å®ç¾©å±¬æ§çç¢çè¦åï¼
using Bogus;
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public string Description { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
}
// 建ç«ç¢åè³æç Faker
var productFaker = new Faker<Product>()
.RuleFor(p => p.Id, f => f.IndexFaker)
.RuleFor(p => p.Name, f => f.Commerce.ProductName())
.RuleFor(p => p.Price, f => f.Random.Decimal(10, 1000))
.RuleFor(p => p.Description, f => f.Lorem.Sentence())
.RuleFor(p => p.CreatedDate, f => f.Date.Past());
// ç¢çå®çè³æ
var product = productFaker.Generate();
// ç¢çå¤çè³æ
var products = productFaker.Generate(10);
å §å»º DataSet æ¦è¦½
Bogus æä¾è±å¯çå §å»º DataSetï¼æ¯åé½å°æ³¨æ¼ç¹å®é åçè³æç¢çï¼
å人è³è¨ (Person DataSet)
var faker = new Faker();
var fullName = faker.Person.FullName; // 宿´å§å
var firstName = faker.Person.FirstName; // åå
var lastName = faker.Person.LastName; // å§æ°
var email = faker.Person.Email; // é»åéµä»¶
var gender = faker.Person.Gender; // æ§å¥
var dateOfBirth = faker.Person.DateOfBirth; // çæ¥
å°åè³è¨ (Address DataSet)
var fullAddress = faker.Address.FullAddress(); // 宿´å°å
var streetAddress = faker.Address.StreetAddress(); // è¡éå°å
var city = faker.Address.City(); // åå¸
var state = faker.Address.State(); // å·/ç
var zipCode = faker.Address.ZipCode(); // éµéåè
var country = faker.Address.Country(); // åå®¶
var latitude = faker.Address.Latitude(); // 緯度
var longitude = faker.Address.Longitude(); // ç¶åº¦
忥è³è¨ (Company & Commerce DataSet)
var companyName = faker.Company.CompanyName(); // å
¬å¸å稱
var catchPhrase = faker.Company.CatchPhrase(); // æ¨èª
var department = faker.Commerce.Department(); // é¨é
var productName = faker.Commerce.ProductName(); // ç¢åå稱
var price = faker.Commerce.Price(1, 1000, 2); // 广 ¼ï¼åä¸²æ ¼å¼ï¼
var ean13 = faker.Commerce.Ean13(); // EAN-13 æ¢ç¢¼
網路è³è¨ (Internet DataSet)
var url = faker.Internet.Url(); // URL
var domainName = faker.Internet.DomainName(); // ç¶²åå稱
var ipAddress = faker.Internet.Ip(); // IPv4 å°å
var ipv6 = faker.Internet.Ipv6(); // IPv6 å°å
var userName = faker.Internet.UserName(); // 使ç¨è
å稱
var password = faker.Internet.Password(); // å¯ç¢¼
var email = faker.Internet.Email(); // é»åéµä»¶
éèè³è¨ (Finance DataSet)
var creditCardNumber = faker.Finance.CreditCardNumber(); // ä¿¡ç¨å¡è
var creditCardCvv = faker.Finance.CreditCardCvv(); // CVV
var account = faker.Finance.Account(); // 帳æ¶è碼
var amount = faker.Finance.Amount(100, 10000, 2); // éé¡
var currency = faker.Finance.Currency(); // 貨幣
var iban = faker.Finance.Iban(); // IBAN
var bic = faker.Finance.Bic(); // BIC/SWIFT
æéè³è¨ (Date DataSet)
var pastDate = faker.Date.Past(); // é廿¥æ
var futureDate = faker.Date.Future(); // æªä¾æ¥æ
var recentDate = faker.Date.Recent(); // æè¿æ¥æ
var soonDate = faker.Date.Soon(); // å³å°å°ä¾çæ¥æ
var between = faker.Date.Between(start, end); // ç¯åå
§æ¥æ
var weekday = faker.Date.Weekday(); // ææå¹¾
鍿©è³æ (Random DataSet)
var randomInt = faker.Random.Int(1, 100); // æ´æ¸
var randomDecimal = faker.Random.Decimal(0, 1000); // å°æ¸
var randomBool = faker.Random.Bool(); // 叿
var randomGuid = faker.Random.Guid(); // GUID
var randomEnum = faker.Random.Enum<DayOfWeek>(); // 鍿©åè
var randomElement = faker.Random.ArrayElement(array); // é£å鍿©å
ç´
var shuffled = faker.Random.Shuffle(collection); // æ´ç
æåå §å®¹ (Lorem DataSet)
var word = faker.Lorem.Word(); // å®å
var words = faker.Lorem.Words(5); // å¤åå®å
var sentence = faker.Lorem.Sentence(); // å¥å
var paragraph = faker.Lorem.Paragraph(); // 段è½
var text = faker.Lorem.Text(); // æååå¡
å¤èªè¨æ¯æ´
Bogus çä¸å¤§ç¹è²æ¯æ¯æ´å¤ç¨®èªè¨åæåï¼è®ç¢ççè³ææ´ç¬¦åç¶å°ç¿æ £ï¼
// ç¹é«ä¸æ
var chineseFaker = new Faker<Person>("zh_TW")
.RuleFor(p => p.Name, f => f.Person.FullName)
.RuleFor(p => p.Address, f => f.Address.FullAddress());
// æ¥æ
var japaneseFaker = new Faker<Person>("ja")
.RuleFor(p => p.Name, f => f.Person.FullName)
.RuleFor(p => p.Phone, f => f.Phone.PhoneNumber());
// æ³æ
var frenchFaker = new Faker<Person>("fr")
.RuleFor(p => p.Name, f => f.Person.FullName)
.RuleFor(p => p.Company, f => f.Company.CompanyName());
æ¯æ´çèªè¨ä»£ç¢¼
| èªè¨ | 代碼 | èªè¨ | 代碼 |
|---|---|---|---|
| è±æï¼ç¾åï¼ | en_US |
ç°¡é«ä¸æ | zh_CN |
| ç¹é«ä¸æ | zh_TW |
æ¥æ | ja |
| éæ | ko |
æ³æ | fr |
| å¾·æ | de |
西ççæ | es |
| ä¿æ | ru |
è¡èçæ | pt_BR |
é²éåè½
æ¶µè Seed å¯éç¾æ§æ§å¶ãæ¢ä»¶å¼ç¢çèæ©çæ§å¶ï¼OrNullãPickRandomWeightedï¼ãéè¯è³æèå·¢çç©ä»¶ãè¤éæ¥åéè¼¯ç´æï¼ä»¥åèªè¨ DataSet æ´å
ï¼å¦å°ç£å¨å°è³æç¢çå¨ï¼ã
ð 宿´å §å®¹è«åé± references/advanced-features.md
Bogus vs AutoFixture æ¯è¼
è¨è¨ç念差ç°
| é ç® | AutoFixture | Bogus |
|---|---|---|
| æ ¸å¿ç念 | å¿å測試 (Anonymous Test) | çå¯¦æ¨¡æ¬ (Realistic Simulation) |
| è³æå質 | 鍿©å¡«å ï¼å°æ³¨æ¸¬è©¦é輯 | ææç¾©è³æï¼æ¨¡æ¬ç實æ å¢ |
| å¸ç¿ææ¬ | èªåæ¨æ·ï¼é¶é ç½® | æç¢ºå®ç¾©ï¼éè¦å¸ç¿ DataSet |
| å¯è®æ§ | æ½è±¡åï¼æ¸å°è³æåªé³ | å ·é«åï¼è³æææç¾© |
é©ç¨å ´æ¯åæ
| å ´æ¯ | 建è°å·¥å · | åå |
|---|---|---|
| å®å 測試 | AutoFixture | å°æ³¨æ¼é輯é©èï¼ä¸éå¿è³æå §å®¹ |
| æ´å測試 | Bogus | éè¦ç實æçè³æé²è¡ç«¯å°ç«¯æ¸¬è©¦ |
| UI åå | Bogus | å±ç¤ºç¨çæ¬çè³æ |
| æè½æ¸¬è©¦ | Bogus | 大éçå¯¦æ ¼å¼çè³æ |
| è³æåº«ç¨®å | Bogus | åå§åéç¼/測試ç°å¢ |
| è¤éç¸ä¾æ§ | AutoFixture | èªåèç循ç°åèåå·¢çç©ä»¶ |
ç¨å¼ç¢¼æ¯è¼
// AutoFixtureï¼ç°¡å®ç´æ¥ï¼èªåæ¨æ·
var fixture = new Fixture();
var user = fixture.Create<User>(); // ä¸è¡æå®ï¼ä½è³æç¡æç¾©
// Bogusï¼éè¦è¨å®ï¼ä½è³æææç¾©
var userFaker = new Faker<User>()
.RuleFor(u => u.Name, f => f.Person.FullName) // ç實çå§åæ ¼å¼
.RuleFor(u => u.Email, f => f.Internet.Email()); // ç實çéµä»¶æ ¼å¼
var user = userFaker.Generate();
æè½æä½³å
éç¨ Faker 實ä¾
public class OptimizedDataGenerator
{
// é ç·¨è¯ Faker 以æåæè½ï¼éæ
æ¬ä½ï¼åªåå§å䏿¬¡ï¼
private static readonly Faker<User> _userFaker = new Faker<User>()
.RuleFor(u => u.Id, f => f.Random.Guid())
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Internet.Email());
public static List<User> GenerateUsers(int count)
=> _userFaker.Generate(count);
}
æ¹æ¬¡ç¢ç
// æ¹æ¬¡ç¢ç以æ¸å°è¨æ¶é«åé
public static IEnumerable<User> GenerateUsersBatch(int totalCount, int batchSize = 1000)
{
var generated = 0;
while (generated < totalCount)
{
var currentBatchSize = Math.Min(batchSize, totalCount - generated);
var batch = _userFaker.Generate(currentBatchSize);
foreach (var user in batch)
{
yield return user;
}
generated += currentBatchSize;
}
}
Lazy åå§å
// ä½¿ç¨ Lazy å»¶é²åå§åè¤éç Faker
private static readonly Lazy<Faker<ComplexEntity>> _complexFaker =
new(() => new Faker<ComplexEntity>()
.RuleFor(e => e.Id, f => f.Random.Guid())
.RuleFor(e => e.Data, f => GenerateComplexData(f)));
public static ComplexEntity Generate() => _complexFaker.Value.Generate();
測試實ä½ç¯ä¾
éµä»¶æå測試
[Fact]
public void EmailService_SendWelcomeEmail_ShouldFormatCorrectly()
{
// Arrange - éè¦ç實ç使ç¨è
è³æä¾æ¸¬è©¦éµä»¶æ ¼å¼
var userFaker = new Faker<User>()
.RuleFor(u => u.Name, f => f.Person.FullName)
.RuleFor(u => u.Email, f => f.Internet.Email());
var user = userFaker.Generate();
var emailService = new EmailService();
// Act
var emailContent = emailService.GenerateWelcomeEmail(user);
// Assert
emailContent.Should().Contain(user.Name);
emailContent.Should().Contain(user.Email);
}
è³æåº«ç¨®å
public static class DatabaseSeeder
{
public static void SeedDatabase(AppDbContext context)
{
// è¨å® seed 確ä¿å¯éç¾
Randomizer.Seed = new Random(42);
var customerFaker = new Faker<Customer>("zh_TW")
.RuleFor(c => c.Name, f => f.Person.FullName)
.RuleFor(c => c.Email, f => f.Internet.Email())
.RuleFor(c => c.Phone, f => f.Phone.PhoneNumber())
.RuleFor(c => c.Address, f => f.Address.FullAddress());
var customers = customerFaker.Generate(100);
context.Customers.AddRange(customers);
context.SaveChanges();
}
}
æä½³å¯¦è¸
å½åèçµç¹
- Faker å½åæ
£ä¾ï¼ä½¿ç¨
{EntityName}Fakeræ ¼å¼å½å - éä¸ç®¡çï¼å° Faker å®ç¾©éä¸å¨
TestDataGeneratorsæFakersè³æå¤¾ - éç¨éæ 實ä¾ï¼é¿å éè¤å»ºç« Faker 實ä¾
ç¨å¼ç¢¼çµç¹
MyProject.Tests/
âââ Fakers/
â âââ CustomerFaker.cs
â âââ OrderFaker.cs
â âââ TaiwanDataSetExtensions.cs
âââ Services/
â âââ CustomerServiceTests.cs
âââ ...
常è¦é·é±
- é¿å é度é ç½®ï¼åªè¨å®æ¸¬è©¦éè¦ç屬æ§
- 注æé¨æ©æ§ï¼ä½¿ç¨ seed ç¢ºä¿æ¸¬è©¦å¯éç¾
- æè½èéï¼å¤§éè³ææä½¿ç¨æ¹æ¬¡ç¢ç
ç¸éæè½
| æè½å稱 | éè¯èªªæ |
|---|---|
autofixture-basics |
AutoFixture åºç¤ä½¿ç¨ï¼é©åå®å 測試çå¿åè³æ |
autofixture-bogus-integration |
AutoFixture è Bogus æ··å使ç¨çç¥ |
test-data-builder-pattern |
æå Builder Patternï¼é©åç°¡å®å ´æ¯ |
åèè³æº
åå§æç«
æ¬æè½å §å®¹æç èªãèæ´¾è»é«å·¥ç¨å¸«ç測試修練 – 30 å¤©ææ°ãç³»åæç« ï¼
- Day 14 – ä½¿ç¨ Bogus ç¢çåè³æ
- éµäººè³½æç« ï¼https://ithelp.ithome.com.tw/articles/10375501
- ç¯ä¾ç¨å¼ç¢¼ï¼https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day14