如何在Xunit中使用Assert.Throws正确编写异常校验单元测试
问题修正说明
你当前代码的核心错误有两点:
Assert.Throws<T>是Xunit专门用来校验异常的API,要求传入会触发异常的执行委托,你直接传入条件表达式/字符串不符合API的调用规则,根本无法生效。- 你的测试用例同时覆盖了「输入匹配不抛异常」「输入不匹配抛指定异常」两种互斥的场景,需要根据入参走不同的断言逻辑,不能只写异常断言。
正确实现方式
你可以直接在Theory用例里根据入参判断分支走不同断言,代码如下:
[Theory] [InlineData("Goods", "Goods")] // 合法输入,执行后不抛异常 [InlineData("Test", "Goods")] // 非法输入,执行后抛参数异常 public void Vehicle(string use, string expectedValidValue) { // Arrange var risk = CreateRisk(); var request = new Request(); risk.Use = use; var testService = Test(); if (use == expectedValidValue) { // 合法场景断言:执行映射方法无异常 var actualEx = Record.Exception(() => testService.Mapping(risk, request)); Assert.Null(actualEx); // 如需校验映射结果正确性,可在这里补充对request字段的断言 } else { // 非法场景断言:抛出指定类型异常,且提示信息符合要求 var actualEx = Assert.Throws<ArgumentException>(() => testService.Mapping(risk, request)); Assert.Contains("传入值必须与预期值保持一致", actualEx.Message); // 如果要匹配你之前写的英文提示,替换成Assert.Contains("Vehicle Use Should be with Goods", actualEx.Message)即可 } }
如果觉得分支判断麻烦,也可以把两种场景拆成独立的测试方法,逻辑更清晰:
[Fact] public void Vehicle_ValidUse_NoExceptionThrown() { // Arrange var risk = CreateRisk(); var request = new Request(); risk.Use = "Goods"; // Act & Assert var ex = Record.Exception(() => Test().Mapping(risk, request)); Assert.Null(ex); } [Fact] public void Vehicle_InvalidUse_ThrowArgumentExceptionWithExpectedMessage() { // Arrange var risk = CreateRisk(); var request = new Request(); risk.Use = "Test"; // Act & Assert var ex = Assert.Throws<ArgumentException>(() => Test().Mapping(risk, request)); Assert.Contains("传入值必须与预期值保持一致", ex.Message); }
注意事项
- 所有可能抛异常的执行逻辑必须包在
Assert.Throws或者Record.Exception的委托参数里,如果你像原来的代码一样先执行Mapping方法,异常会在进入断言前就直接抛出,测试直接失败,根本走不到校验逻辑。 Record.Exception是Xunit内置的异常捕获工具,会返回委托执行过程中抛出的异常,没有异常就返回null,非常适合用来校验「某段代码不抛异常」的场景。
内容的提问来源于stack exchange,提问作者shreyas35
相关产品推荐
相关产品推荐

