xUnit Assert.Throws 和 Record.Exception 没有捕获异常

xUnit Assert.Throws and Record.Exception does not catch exception

在编写单元测试用例以测试抛出的异常时,尝试使用以下两种使用 xUnit 的方法

  1. Assert.Throws(动作)
  2. Record.Exception(动作)

代码如下

public class Me : Entity, IAggregateRoot
{
   public string Name {get; }

   private List<Friend> friends;
   public IReadonlyCollection<Friend> Friends => friends;

   public Me(string name)
   {
      Name = name;
      friends = new List<Friend>();
   }

   public void AddFriend(Friend friend)
   {
     if(friend.Type != "UnKnown")
        friend.Add(friend);
     else
        throw new Exception("Cannot add a unknown friend.");
   }
}

public class Friend
{
    public string Name {get; }
    public string Type {get; }

    public Friend(string name, string type)
    {
        Name = name;
        Type = type;
    }
}


using System;
using MediatR;
using System.Collections.Generic;

public abstract class Entity
{
    int? _requestedHashCode;
    int _Id;        
    public virtual  int Id 
    {
        get
        {
            return _Id;
        }
        protected set
        {
            _Id = value;
        }
    }

    private List<INotification> _domainEvents;
    public IReadOnlyCollection<INotification> DomainEvents => _ domainEvents?.AsReadOnly();

    public void AddDomainEvent(INotification eventItem)
    {
        _domainEvents = _domainEvents ?? new List<INotification>();
        _domainEvents.Add(eventItem);
    }

    public void RemoveDomainEvent(INotification eventItem)
    {
        _domainEvents?.Remove(eventItem);
    }

    public void ClearDomainEvents()
    {
        _domainEvents?.Clear();
    }

    public bool IsTransient()
    {
        return this.Id == default(Int32);
    }

    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is Entity))
            return false;

        if (Object.ReferenceEquals(this, obj))
            return true;

        if (this.GetType() != obj.GetType())
            return false;

        Entity item = (Entity)obj;

        if (item.IsTransient() || this.IsTransient())
            return false;
        else
            return item.Id == this.Id;
    }

    public override int GetHashCode()
    {
        if (!IsTransient())
        {
            if (!_requestedHashCode.HasValue)
                _requestedHashCode = this.Id.GetHashCode() ^ 31; 

            return _requestedHashCode.Value;
        }
        else
            return base.GetHashCode();

    }
    public static bool operator ==(Entity left, Entity right)
    {
        if (Object.Equals(left, null))
            return (Object.Equals(right, null)) ? true : false;
        else
            return left.Equals(right);
    }

    public static bool operator !=(Entity left, Entity right)
    {
        return !(left == right);
    }
}

public interface IAggregateRoot
{}//its empty

测试用例 1:使用 Assert.Throws

[Fact]
public void add_friend_business_rule_validation_throws_exception1()
{
   var me = new Me("mady");
   var friend = new Friend("johnDoe","UnKnown");

   Assert.Throws<Exception>(() => me.AddFriend(friend));
}

测试用例 2:使用 Record.Exception

[Fact]
public void add_friend_business_rule_validation_throws_exception()
{
   var me = new Me("mady");
   var friend = new Friend("johnDoe","UnKnown");

   var ex = Record.Exception(() => me.AddFriend(friend));
   Assert.NotNull(ex);
   Assert.IsType<Exception>(ex);
}

我想编写一个单元测试用例来测试所提供代码的异常。我的期望是代码应该在尝试将实体添加到集合时在验证失败时抛出异常。

问题: 这里的测试用例没有失败也没有通过。执行停在下一行。

  throw new Exception("Cannot add a unknown friend.");

当您没有 try catch 块来捕获它时,就会发生这种情况。我期待 xUnit Assert.Throws & Record.Exception 会捕获异常,但事实并非如此。

注意:提供的代码与实际代码非常接近,实体名称有所不同,并通过删除不必要的代码进行了简化。

我看过以下文档

  1. xUnit-Record.Exception Documentation

编辑: 在我 运行 测试时,xUnit Assert.Throws & Record.Exception 的行为符合预期。

调试测试有问题。

如前所述,测试用例既没有失败也没有通过。每当任何代码行抛出异常时,最近的 catch 块都应捕获异常。根据文档和用户体验,我对 xUnit Assert.Throws & Record.Exception 有类似的期望。然而在调试测试时却不是这样。

证明问题和行为预期的另一种众所周知的方法。

[Fact]
public void add_friend_business_rule_validation_throws_exception1()
{
   var me = new Me("mady");
   var friend = new Friend("johnDoe","UnKnown");
   Exception expectedException = null;
   try
   { 
       me.AddFriend(friend); //exception thrown block
   }
   catch(Exception ex)//exception catched - control not stopped 
   {
       expectedException = ex;
   }

   Assert.NotNull(expectedException);
   Assert.IsType<Exception>(expectedException);
}

我 运行 测试时 xUnit Assert.Throws & Record.Exception 的行为符合预期。

调试测试有问题。

我能够通过从抛出的异常行跳过来调试测试。看到控件停在该行后,按F8或F5继续,会执行测试。

throw new Exception("Cannot add a unknown friend.");