dotnet-testing-autodata-xunit-integration — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dotnet-testing-autodata-xunit-integration (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
AutoData 屬性家族是 AutoFixture.Xunit2 套件提供的功能,將 AutoFixture 的資料產生能力與 xUnit 的參數化測試整合,讓測試參數自動注入,大幅減少測試準備程式碼。
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" />dotnet add package AutoFixture.Xunit2AutoData 是最基礎的屬性,自動為測試方法的所有參數產生測試資料。
using AutoFixture.Xunit2;
public class Person
{
public Guid Id { get; set; }
[StringLength(10)]
public string Name { get; set; } = string.Empty;
[Range(18, 80)]
public int Age { get; set; }
public string Email { get; set; } = string.Empty;
public DateTime CreateTime { get; set; }
}
[Theory]
[AutoData]
public void AutoData_應能自動產生所有參數(Person person, string message, int count)
{
// Arrange & Act - 參數已由 AutoData 自動產生
// Assert
person.Should().NotBeNull();
person.Id.Should().NotBe(Guid.Empty);
person.Name.Should().HaveLength(10); // 遵循 StringLength 限制
person.Age.Should().BeInRange(18, 80); // 遵循 Range 限制
message.Should().NotBeNullOrEmpty();
count.Should().NotBe(0);
}[Theory]
[AutoData]
public void AutoData_透過DataAnnotation約束參數(
[StringLength(5, MinimumLength = 3)] string shortName,
[Range(1, 100)] int percentage,
Person person)
{
// Assert
shortName.Length.Should().BeInRange(3, 5);
percentage.Should().BeInRange(1, 100);
person.Should().NotBeNull();
}InlineAutoData 結合了 InlineData 的固定值特性與 AutoData 的自動產生功能。
[Theory]
[InlineAutoData("VIP客戶", 1000)]
[InlineAutoData("一般客戶", 500)]
[InlineAutoData("新客戶", 100)]
public void InlineAutoData_混合固定值與自動產生(
string customerType, // 對應第 1 個固定值
decimal creditLimit, // 對應第 2 個固定值
Person person) // 由 AutoFixture 產生
{
// Arrange
var customer = new Customer
{
Person = person,
Type = customerType,
CreditLimit = creditLimit
};
// Assert
customer.Type.Should().Be(customerType);
customer.CreditLimit.Should().BeOneOf(1000, 500, 100);
customer.Person.Should().NotBeNull();
}固定值的順序必須與方法參數順序一致:
[Theory]
[InlineAutoData("產品A", 100)] // 依序對應 name, price
[InlineAutoData("產品B", 200)]
public void InlineAutoData_參數順序一致性(
string name, // 對應第 1 個固定值
decimal price, // 對應第 2 個固定值
string description, // 由 AutoFixture 產生
Product product) // 由 AutoFixture 產生
{
// Assert
name.Should().BeOneOf("產品A", "產品B");
price.Should().BeOneOf(100, 200);
description.Should().NotBeNullOrEmpty();
product.Should().NotBeNull();
}// 正確:使用常數
[InlineAutoData("VIP", 100000)]
[InlineAutoData("Premium", 50000)]
// 錯誤:不能使用變數
private const decimal VipCreditLimit = 100000m;
[InlineAutoData("VIP", VipCreditLimit)] // 編譯錯誤
// 錯誤:不能使用運算式
[InlineAutoData("VIP", 100 * 1000)] // 編譯錯誤需要使用複雜資料時,應使用 MemberAutoData。
MemberAutoData 允許從類別的方法、屬性或欄位中獲取測試資料。
public class MemberAutoDataTests
{
public static IEnumerable<object[]> GetProductCategories()
{
yield return new object[] { "3C產品", "TECH" };
yield return new object[] { "服飾配件", "FASHION" };
yield return new object[] { "居家生活", "HOME" };
yield return new object[] { "運動健身", "SPORTS" };
}
[Theory]
[MemberAutoData(nameof(GetProductCategories))]
public void MemberAutoData_使用靜態方法資料(
string categoryName, // 來自 GetProductCategories
string categoryCode, // 來自 GetProductCategories
Product product) // 由 AutoFixture 產生
{
// Arrange
var categorizedProduct = new CategorizedProduct
{
Product = product,
CategoryName = categoryName,
CategoryCode = categoryCode
};
// Assert
categorizedProduct.CategoryName.Should().Be(categoryName);
categorizedProduct.CategoryCode.Should().Be(categoryCode);
categorizedProduct.Product.Should().NotBeNull();
}
}public static IEnumerable<object[]> StatusTransitions => new[]
{
new object[] { OrderStatus.Created, OrderStatus.Confirmed },
new object[] { OrderStatus.Confirmed, OrderStatus.Shipped },
new object[] { OrderStatus.Shipped, OrderStatus.Delivered },
new object[] { OrderStatus.Delivered, OrderStatus.Completed }
};
[Theory]
[MemberAutoData(nameof(StatusTransitions))]
public void MemberAutoData_使用靜態屬性_訂單狀態轉換(
OrderStatus fromStatus,
OrderStatus toStatus,
Order order)
{
// Arrange
order.Status = fromStatus;
// Act
order.TransitionTo(toStatus);
// Assert
order.Status.Should().Be(toStatus);
}建立專屬的 AutoData 配置:
public class DomainAutoDataAttribute : AutoDataAttribute
{
public DomainAutoDataAttribute() : base(() => CreateFixture())
{
}
private static IFixture CreateFixture()
{
var fixture = new Fixture();
// 設定 Person 的自訂規則
fixture.Customize<Person>(composer => composer
.With(p => p.Age, () => Random.Shared.Next(18, 65))
.With(p => p.Email, () => $"user{Random.Shared.Next(1000)}@example.com")
.With(p => p.Name, () => $"測試用戶{Random.Shared.Next(100)}"));
// 設定 Product 的自訂規則
fixture.Customize<Product>(composer => composer
.With(p => p.Price, () => Random.Shared.Next(100, 10000))
.With(p => p.IsAvailable, true)
.With(p => p.Name, () => $"產品{Random.Shared.Next(1000)}"));
return fixture;
}
}public class BusinessAutoDataAttribute : AutoDataAttribute
{
public BusinessAutoDataAttribute() : base(() => CreateFixture())
{
}
private static IFixture CreateFixture()
{
var fixture = new Fixture();
// 設定 Order 的業務規則
fixture.Customize<Order>(composer => composer
.With(o => o.Status, OrderStatus.Created)
.With(o => o.Amount, () => Random.Shared.Next(1000, 50000))
.With(o => o.OrderNumber, () => $"ORD{DateTime.Now:yyyyMMdd}{Random.Shared.Next(1000):D4}"));
return fixture;
}
}[Theory]
[DomainAutoData]
public void 使用DomainAutoData(Person person, Product product)
{
person.Age.Should().BeInRange(18, 64);
person.Email.Should().EndWith("@example.com");
product.IsAvailable.Should().BeTrue();
}
[Theory]
[BusinessAutoData]
public void 使用BusinessAutoData(Order order)
{
order.Status.Should().Be(OrderStatus.Created);
order.Amount.Should().BeInRange(1000, 49999);
order.OrderNumber.Should().StartWith("ORD");
}透過繼承 AutoDataAttribute 並以反射合併多個 Fixture 的 Customizations,組合多個自訂 AutoData 配置:
// 使用方式 - 自動套用 DomainAutoData + BusinessAutoData 的所有設定
[Theory]
[CompositeAutoData(typeof(DomainAutoDataAttribute), typeof(BusinessAutoDataAttribute))]
public void CompositeAutoData_整合多重資料來源(
Person person, Product product, Order order)
{
person.Age.Should().BeInRange(18, 64); // DomainAutoData 的設定
product.IsAvailable.Should().BeTrue(); // DomainAutoData 的設定
order.Status.Should().Be(OrderStatus.Created); // BusinessAutoData 的設定
}AutoData 預設的集合大小是 3,可透過自訂 CollectionSizeAttribute 控制產生的集合元素數量。完整實作與使用方式請參考 CollectionSizeAttribute 完整指南。
整合 CSV、JSON 等外部資料來源與 MemberAutoData,同時保留 AutoFixture 自動產生其餘參數的能力。完整指南請參考 外部測試資料整合。
階層式資料組織與可重用資料集的設計模式,建立 BaseTestData 基底類別統一管理測試資料路徑。完整範例請參考 資料來源設計模式。
[Theory]
[InlineAutoData("VIP", 100000)]
[InlineAutoData("Premium", 50000)]
[InlineAutoData("Regular", 20000)]
public void AutoData與AwesomeAssertions協作_客戶等級驗證(
string customerLevel,
decimal expectedCreditLimit,
[Range(1000, 15000)] decimal orderAmount,
Customer customer,
Order order)
{
// Arrange
customer.Type = customerLevel;
customer.CreditLimit = expectedCreditLimit;
order.Amount = orderAmount;
// Act
var canPlaceOrder = customer.CanPlaceOrder(order.Amount);
var discountRate = CalculateDiscount(customer.Type, order.Amount);
// Assert - 使用 Awesome Assertions 語法
customer.Type.Should().Be(customerLevel);
customer.CreditLimit.Should().Be(expectedCreditLimit);
customer.CreditLimit.Should().BePositive();
order.Amount.Should().BeInRange(1000m, 15000m);
canPlaceOrder.Should().BeTrue();
discountRate.Should().BeInRange(0m, 0.3m);
}
private static decimal CalculateDiscount(string customerType, decimal orderAmount)
{
var baseDiscount = customerType switch
{
"VIP" => 0.15m,
"Premium" => 0.10m,
"Regular" => 0.05m,
_ => 0m
};
var largeOrderBonus = orderAmount > 30000m ? 0.05m : 0m;
return Math.Min(baseDiscount + largeOrderBonus, 0.3m);
}[StringLength]、[Range] 約束資料範圍請參考 templates 資料夾中的範例檔案:
[Theory] 搭配 [AutoData] 或 [InlineAutoData] 的測試方法AutoDataAttribute 衍生類別檔案(*AutoDataAttribute.cs)本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.