2

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);
}
Mady
  • 459
  • 1
  • 11
  • 26
  • Is the shown example code an accurate representation of the actual code? Are you using async task any where in the actual code? – Nkosi Mar 03 '19 at 20:51
  • Yes, it is. No async task used. – Mady Mar 03 '19 at 21:06
  • I just tested the first scenario using provided code and it passes as expected. Had to fix a few minor syntax errors, but it behaves as expected when the test is exercised. – Nkosi Mar 03 '19 at 21:10
  • I have updated Me class with actual base class. – Mady Mar 03 '19 at 21:28
  • This might be an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Provide a [mcve] that clarifies your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. – Nkosi Mar 03 '19 at 21:35
  • What does actual `AddFriend` look like. Most likely you are either asserting or returning the wrong exception type – Nkosi Mar 03 '19 at 21:36
  • I have updated the description and provided more details, please check. – Mady Mar 03 '19 at 22:13
  • Ran it again with the additional code and it works as expected. I suggest then you try creating a new project with the code and see if the problem persists. As it currently stands, I am unable to reproduce the issue. – Nkosi Mar 03 '19 at 23:28

1 Answers1

1

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 an unknown friend.");
Enrico
  • 2,734
  • 1
  • 27
  • 40
Mady
  • 459
  • 1
  • 11
  • 26