You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MSTest数据驱动测试中混合使用不同断言类型

MSTest数据驱动测试整合正常场景与异常场景

核心结论

完全可以把正常返回断言和异常断言整合到同一个DynamicData数据驱动测试中,同时实现用例与测试代码的解耦。

实现思路

  1. 封装测试用例模型:创建类来统一管理每个测试用例的输入、预期结果(正常场景)、预期异常类型(异常场景),实现用例的独立维护。
  2. 独立维护测试数据源:把所有正常/异常场景的用例整理成静态数据源,与测试逻辑分离。
  3. 编写统一测试逻辑:在单个测试方法中,根据用例配置自动判断执行正常结果断言还是异常断言。

具体实现代码

1. 定义测试用例模型

public class ApprovalCreationTestCase
{
    // 用例名称,方便测试时识别场景
    public string TestCaseName { get; set; }
    // 用于创建测试输入对象的工厂方法,避免用例间状态污染
    public Func<Approval> ApprovalFactory { get; set; }
    // 正常场景下的预期最小ID(对应原代码中ID>0的断言)
    public long ExpectedMinValidId { get; set; } = 0;
    // 异常场景下的预期异常类型,正常场景设为null
    public Type ExpectedExceptionType { get; set; }
}

2. 准备静态测试用例数据源

public static IEnumerable<object[]> ApprovalCreationTestCases
{
    get
    {
        // 正常场景:有效用户+合法按钮文本
        yield return new object[]
        {
            new ApprovalCreationTestCase
            {
                TestCaseName = "正常创建审批-有效用户与按钮",
                ApprovalFactory = () => 
                {
                    var approval = CreateSendToApproverTestCase();
                    approval.SignTicketId = signTicketId; // 假设signTicketId为测试类成员变量
                    approval.AssignedTo = "alice ; ";
                    approval.Buttons[0].Text = "默认按钮"; // 恢复合法按钮文本
                    return approval;
                },
                ExpectedMinValidId = 0,
                ExpectedExceptionType = null
            }
        };

        // 异常场景1:用户不存在
        yield return new object[]
        {
            new ApprovalCreationTestCase
            {
                TestCaseName = "创建审批失败-指定用户不存在",
                ApprovalFactory = () => 
                {
                    var approval = CreateSendToApproverTestCase();
                    approval.SignTicketId = signTicketId;
                    approval.AssignedTo = "alice ; aabbb ";
                    return approval;
                },
                ExpectedExceptionType = typeof(UserNotExistException)
            }
        };

        // 异常场景2:用户为空
        yield return new object[]
        {
            new ApprovalCreationTestCase
            {
                TestCaseName = "创建审批失败-用户为空字符串",
                ApprovalFactory = () => 
                {
                    var approval = CreateSendToApproverTestCase();
                    approval.SignTicketId = signTicketId;
                    approval.AssignedTo = "";
                    return approval;
                },
                ExpectedExceptionType = typeof(NullUserException)
            }
        };

        // 异常场景3:按钮文本非法
        yield return new object[]
        {
            new ApprovalCreationTestCase
            {
                TestCaseName = "创建审批失败-按钮文本格式非法",
                ApprovalFactory = () => 
                {
                    var approval = CreateSendToApproverTestCase();
                    approval.SignTicketId = signTicketId;
                    approval.AssignedTo = "alice";
                    approval.Buttons[0].Text = "__Cancel";
                    return approval;
                },
                ExpectedExceptionType = typeof(ButtonTextException)
            }
        };
    }
}

3. 编写统一的测试方法

[TestClass]
public class ApprovalServiceTests
{
    private IApprovalService _approvalService;
    private long signTicketId; // 可在TestInitialize中初始化依赖

    [TestInitialize]
    public void TestInitialize()
    {
        _approvalService = new ApprovalService(); // 实例化待测试服务
        // 初始化signTicketId等测试依赖数据
    }

    [TestMethod]
    [DynamicData(nameof(ApprovalCreationTestCases), DynamicDataSourceType.Property)]
    public void CreateApproval_ShouldBehaveAsExpected(ApprovalCreationTestCase testCase)
    {
        var approval = testCase.ApprovalFactory();

        if (testCase.ExpectedExceptionType != null)
        {
            // 异常场景:断言抛出指定类型的异常
            Assert.ThrowsException(testCase.ExpectedExceptionType, 
                () => _approvalService.CreateApproval(approval, false));
        }
        else
        {
            // 正常场景:执行方法并验证返回结果
            var newApproval = _approvalService.CreateApproval(approval);
            Assert.IsTrue(newApproval.SignTicketApprovalId.ToLong() > testCase.ExpectedMinValidId);
            Thread.Sleep(3000); // 原代码中的等待逻辑,如需灵活控制可添加到用例模型
        }
    }

    // 原有的测试对象初始化方法
    private Approval CreateSendToApproverTestCase()
    {
        return new Approval
        {
            Buttons = new List<Button> { new Button { Text = "默认按钮" } }
            // 其他必要属性初始化
        };
    }
}

关键细节说明

  • 用例解耦:所有测试场景都集中在ApprovalCreationTestCases静态属性中,后续新增/修改用例只需调整数据源,无需修改测试逻辑。
  • 状态隔离:用Func<Approval>工厂方法创建输入对象,确保每个用例使用独立的实例,避免不同用例间的状态污染。
  • 可读性提升:每个用例设置TestCaseName,测试运行时可直接看到场景名称,便于快速定位问题。

内容的提问来源于stack exchange,提问作者qwer11121

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:01:16