xUnit Assert.Throws и Record.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
  2. Заявить об исключении с помощью вопроса XUnit- Stackoverflow

РЕДАКТИРОВАТЬ: Во время выполнения тестов поведение 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);
}

person Mady    schedule 03.03.2019    source источник
comment
Является ли показанный пример кода точным представлением реального кода? Используете ли вы асинхронную задачу где-нибудь в реальном коде?   -  person Nkosi    schedule 03.03.2019
comment
Да, это. Асинхронная задача не используется.   -  person Mady    schedule 04.03.2019
comment
Я только что протестировал первый сценарий, используя предоставленный код, и он прошел, как ожидалось. Пришлось исправить несколько мелких синтаксических ошибок, но при выполнении теста он ведет себя должным образом.   -  person Nkosi    schedule 04.03.2019
comment
Я обновил свой класс фактическим базовым классом.   -  person Mady    schedule 04.03.2019
comment
Это может быть проблема XY. Предоставьте минимальный воспроизводимый пример, который проясняет вашу конкретную проблему, или добавьте дополнительные сведения, чтобы выделить именно то, что вам нужно. В настоящее время трудно сказать, о чем вы спрашиваете.   -  person Nkosi    schedule 04.03.2019
comment
Как выглядит реальный AddFriend. Скорее всего, вы либо утверждаете, либо возвращаете неправильный тип исключения   -  person Nkosi    schedule 04.03.2019
comment
Я обновил описание и предоставил более подробную информацию, проверьте.   -  person Mady    schedule 04.03.2019
comment
Запустите его еще раз с дополнительным кодом, и он работает, как ожидалось. Я предлагаю вам попробовать создать новый проект с кодом и посмотреть, сохраняется ли проблема. В настоящее время я не могу воспроизвести проблему.   -  person Nkosi    schedule 04.03.2019


Ответы (1)


Во время выполнения тестов поведение xUnit Assert.Throws & Record.Exception соответствует ожидаемому.

Проблема связана с отладочными тестами.

Мне удалось отладить тест, перейдя из строки сгенерированного исключения. После того, как вы увидите, что элемент управления остановился на линии, нажмите F8 или F5, чтобы продолжить, и тест будет выполнен.

throw new Exception("Cannot add a unknown friend.");
person Mady    schedule 04.03.2019