简体   繁体   中英

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

While writing unit test case to test thrown exception, have tried to use below both methods using xUnit

  1. Assert.Throws(action)
  2. Record.Exception(action)

Following is the code

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

Test Case 1: Using 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));
}

Test Case 2: Using 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);
}

I would like to write a unit test case to test exception for the provided code. My expectation is the code should throw exception on validation fail when trying to add a entity to a collection.

Issue: The test cases here are not failing neither passing. The execution is stalled at following line.

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

This will happen when you have no try catch block to catch it. I'm expecting xUnit Assert.Throws & Record.Exception will catch the exception but it is not.

Note: The code provided is very close to the actual code, with entity names being different and simplified by removing unnecessary code.

I have seen the following documentation

  1. xUnit-Record.Exception Documentation
  2. Assert an Exception using XUnit- Stackoverflow question

EDIT: The xUnit Assert.Throws & Record.Exception behavior is as expected while I Run Tests.

Issue is with debugging tests.

As mentioned test cases were are not failing neither passing. Whenever any line of code throws exception the nearest catch block shall catch the exception. I have similar expectation from xUnit Assert.Throws & Record.Exception based on documentation and user experience. However this is not the case while debugging test.

An alternate & well known approach to demonstrate the problem and expectation of behavior.

[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);
}

The xUnit Assert.Throws & Record.Exception behavior is as expected while I Run Tests.

Issue is with debugging tests.

I was able to debug the test by stepping over from the exception thrown line. After you see the control stopped at the line, press F8 or F5 to continue and test will be executed.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM