在C#的单元测试场景中,xUnit框架的Theory特性是实现数据驱动参数化测试的核心工具,它允许同一个测试方法接收多组不同的输入数据,自动执行多次测试验证,避免为相似逻辑编写重复的测试方法。

xUnit Theory基础概念
xUnit中的Fact特性用于标记无参数的固定测试,而Theory特性则用于标记参数化测试,需要配合数据提供特性使用,才能传入不同的测试数据。Theory测试的核心逻辑是:测试方法定义参数,数据提供特性为参数提供多组值,框架会自动为每组数据执行一次测试方法。
常用数据提供方式
1. InlineData提供简单数据
InlineData是最简单的数据提供方式,适合传入少量、简单的测试数据,直接在特性中定义每组参数的取值。
using Xunit;
public class CalculatorTest
{
// 测试方法接收两个int参数,InlineData提供三组不同的输入数据
[Theory]
[InlineData(1, 2, 3)]
[InlineData(5, 3, 8)]
[InlineData(-1, 1, 0)]
public void Add_ShouldReturnCorrectSum(int a, int b, int expected)
{
var calculator = new Calculator();
var result = calculator.Add(a, b);
Assert.Equal(expected, result);
}
}
// 简单的计算器类
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
}
2. MemberData提供复杂数据
当测试数据比较复杂,或者需要复用时,可以使用MemberData特性,它支持从类的静态属性、字段、方法中获取测试数据。
using Xunit;
using System.Collections.Generic;
public class StringHelperTest
{
// 静态属性提供测试数据,返回IEnumerable<object[]>类型
public static IEnumerable<object[]> SplitTestData
{
get
{
yield return new object[] { "a,b,c", ',', new string[] { "a", "b", "c" } };
yield return new object[] { "hello|world", '|', new string[] { "hello", "world" } };
yield return new object[] { "test", ';', new string[] { "test" } };
}
}
[Theory]
[MemberData(nameof(SplitTestData))]
public void Split_ShouldReturnCorrectArray(string input, char separator, string[] expected)
{
var helper = new StringHelper();
var result = helper.Split(input, separator);
Assert.Equal(expected, result);
}
}
public class StringHelper
{
public string[] Split(string input, char separator)
{
return input.Split(separator);
}
}
3. ClassData提供独立数据类
如果测试数据逻辑比较复杂,或者需要在多个测试类之间共享,可以定义独立的数据类,实现IEnumerable<object[]>接口,通过ClassData特性引用。
using Xunit;
using System.Collections.Generic;
public class UserAgeTest
{
[Theory]
[ClassData(typeof(UserAgeTestData))]
public void ValidateAge_ShouldReturnCorrectResult(int age, bool expected)
{
var validator = new UserValidator();
var result = validator.ValidateAge(age);
Assert.Equal(expected, result);
}
}
// 独立的测试数据类
public class UserAgeTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { 18, true };
yield return new object[] { 20, true };
yield return new object[] { 15, false };
yield return new object[] { -1, false };
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class UserValidator
{
public bool ValidateAge(int age)
{
return age >= 18 && age <= 120;
}
}
注意事项
- Theory测试的数据组如果执行失败,xUnit会明确标记出是哪一组输入数据导致的失败,方便定位问题。
- 使用MemberData和ClassData时,返回的数据类型必须是
IEnumerable<object[]>,每组数据的元素顺序要和测试方法的参数顺序对应。 - 如果测试数据需要读取外部文件或者数据库,也可以在数据提供方法中实现对应的读取逻辑,再返回测试数据集合。
常见问题解答
为什么Theory测试没有执行?
首先检查是否给Theory特性添加了对应的数据提供特性,比如InlineData、MemberData等,没有数据提供的Theory测试会被xUnit忽略,不会执行。
数据提供方法支持异步吗?
默认的数据提供方式不支持异步,如果需要异步获取测试数据,可以使用第三方扩展或者自定义数据提供逻辑,在测试方法中处理异步数据获取。
xUnit_TheoryC#数据驱动测试参数化测试修改时间:2026-07-22 12:06:24