【问题标题】:How to assert exception type and exception message?如何断言异常类型和异常消息?
【发布时间】:2021-07-06 06:15:45
【问题描述】:

这是我在 XUnit 中的测试方法。

    [Fact]
    public async Task AddCampaign_ReturnBadRequestWhenDateIsInvalid()
    {
        var client = _factory.CreateClient();
        string title = string.Format("Test Add Campaign {0}", Guid.NewGuid());
        var campaignAddDto = new CampaignDTORequest
        {
            Title = title
        };
        var encodedContent = new StringContent(JsonConvert.SerializeObject(campaignAddDto), Encoding.UTF8, "application/json");

        var response = await client.PostAsync("/api/Campaign/add", encodedContent);
        var responseString = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<CampaignDTOResponse>(responseString);

        Assert.False(response.IsSuccessStatusCode);
        Assert.ThrowsAsync<ArgumentNullException>(()=> client.PostAsync("/api/Campaign/add", encodedContent));
    }

第一个断言是有效的。我坚持第二个断言。如何断言异常类型 (ArgumentNullException) 及其异常消息?

这是服务方法

    public async Task<Campaign> AddCampaignAsync(Campaign campaign)
    {            
        if (campaign.StartDate.Equals(DateTime.MinValue)) {
            throw new ArgumentNullException("Start Date cannot be null or empty.");
        }
        
        await _context.Campaigns.AddAsync(campaign);
        await _context.SaveChangesAsync();
        return campaign;
    }

根据雷洋的线索更新。

var exceptionDetails = Assert.ThrowsAsync<ArgumentNullException>(() => client.PostAsync("/api/Campaign/add", encodedContent));
Assert.Equal("Start Date cannot be null or empty.", exceptionDetails.Result.Message);

但还是不行。

System.AggregateException :发生一个或多个错误。 (Assert.Throws() 失败 预期:typeof(System.ArgumentNullException) 实际:(没有抛出异常))

尝试了 Dai 的解决方案,但仍然出现错误。

Assert.Throws() Failure
Expected: typeof(System.ArgumentNullException)
Actual:   (No exception was thrown)

这是我的 API 方法。

 public async Task<ActionResult<CampaignDTOResponse>> AddCampaign([FromBody] CampaignDTORequest newCampaign)
    {
        try
        {
            var campaign = _mapper.Map<Campaign>(newCampaign);
            campaign = await _campaignService.AddCampaignAsync(campaign);
            var campaignDtoResponse = _mapper.Map<CampaignDTOResponse>(campaign);
            return CreatedAtAction(nameof(GetCampaignById), new { id = campaignDtoResponse.Id }, campaignDtoResponse);
        }
        catch (Exception ex)
        {
            _logger.LogError(0, ex, ex.Message);
            return Problem(ex.Message);
        }
    }

更新:我将检查从服务移到 api。

if (newCampaign.StartDate.Equals(DateTime.MinValue))
{
    return BadRequest("Start Date cannot be null or empty.");
}

我像下面这样断言它们。

Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal("Start Date cannot be null or empty.", responseString);

【问题讨论】:

  • Assert.Throws 有一个返回类型
  • ^^ 它返回异常实例,然后您可以在该实例上断言其消息。顺便说一句:我个人爱上了“FluentAssertions”库。它使得提出这种断言对测试人员非常友好。 (非附属)
  • @Steve 你需要await它。
  • @Steve API 方法使这种 x-y。该方法不会抛出任何东西。因此,即使您的断言 100% 正确,它们也会失败,因为您的测试对象的行为与您的预期不同。
  • @Steve 当campaign 不是不是null 时,你的代码会抛出ArgumentNullException(否则它不能取消引用StartDate.Equals)——这简直就是错误。请仔细阅读如何正确表示和执行前提条件。

标签: c# xunit


【解决方案1】:

Assert.ThrowsAsync is still an async Task method,因此您需要await 以确保任务继续(执行实际断言)可以正确运行:

[Fact]
public async Task AddCampaign_Return_bad_request_when_date_is_invalid()
{
    [...]

    Assert.False(response.IsSuccessStatusCode);
    await Assert.ThrowsAsync<ArgumentNullException>(()=> client.PostAsync("/api/Campaign/add", encodedContent));
}

然而……

  • ....请重新考虑您的设计:Exceptions should be exceptional。
  • 即使你想抛出,也不应该抛出 ArgumentNullException 来表示 HTTP 400 错误请求响应。
    • ArgumentException 类及其子类(ArgumentNullException、ArgumentOutOfRangeException 等)应仅用于指示失败的前置条件 - 而不是失败的后置条件或内部错误(使用 InvalidOperationException)。李>
    • 就我个人而言,我不认为 Web 服务客户端应该永远为任何响应抛出异常,除非它实际上是一个“异常”响应或情况。
    • 如果您使用 NSwag 生成 Web 服务客户端,那么它会为您生成 ApiException&lt;TResponse&gt;,这非常有用。
    • 虽然我的偏好是返回所有合理可能的响应(即,[ProducesResponseType] 声明的任何响应)的有区别的联合。

【讨论】:

  • 我建议添加将返回分配给 var 的部分并断言消息,只是为了完全回答问题?
  • @Fildor 更好的主意:OP 不应该为此使用ArgumentNullException - 我们不应该测试Exception.Message,因为它是可本地化的。此外,非异常情况不应由异常表示。
  • @Steve 如果你捕捉到它可能抛出的任何异常,你如何期望该方法抛出任何东西?
  • @Steve Assert.Throws 只能断言未捕获的异常。 Assert.Throws 不验证异常是否被抛出并捕获。为此,您需要连接到 AppDomain,您不应该在此类测试中这样做。
【解决方案2】:

您可以使用ThrowsAsync 捕获异常并声明它:

// Act
var exception = await Assert.ThrowsAsync<ArgumentNullException>(()=> client.PostAsync("/api/Campaign/add", encodedContent));

// Assert
Assert.Equal(exception.Message, "message to compare");

或

// Act
Action action = async () => await client.PostAsync("/api/Campaign/add", encodedContent);
var ex = Record.Exception(action);

// Assert
Assert.NotNull(ex);
Assert.IsType<ArgumentNullException>(ex);

【讨论】:

  • 不确定第二个 sn-p 对于async 是否正确?
  • @mohsen,我试过你的代码(第二个),但还是不行。 Assert.IsType() 失败。预期:System.ArgumentNullException。实际:(空)
  • 我想弥补下降。忘记“等待”...您可以删除评论或简单地对其进行编辑。在这种情况下,没有评论的否决是不公平的。
  • @Steve 如果您没有重新尝试/捕获,您的方法仍然不会抛出任何东西:D
  • @MohsenEsmailpour 他的 API 方法实际上并没有抛出任何异常。
猜你喜欢
  • 2018-10-29
  • 2018-09-26
  • 1970-01-01
  • 2021-06-23
  • 2010-12-09
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多