Тест Autofixture для недопустимого параметра конструктора

У меня есть следующий класс и тест. Я хочу протестировать передачу нулевого значения в качестве параметра конструктору и ожидать ArgumentNullException. Но поскольку я использую метод Autofixture CreateAnonymous, я получаю вместо него TargetInvocationException.

Каков правильный способ написания таких тестов?

public sealed class CreateObject : Command {
    // Properties
    public ObjectId[] Ids { get; private set; }
    public ObjectTypeId ObjectType { get; private set; }
    public UserId CreatedBy { get; private set; }

    // Constructor
    public CreateObject(ObjectId[] ids, ObjectTypeId objectType, UserId createdBy) {
      Guard.NotNull(ids, "ids");
      Guard.NotNull(objectType, "objectType");
      Guard.NotNull(createdBy, "createdBy");

      Ids = ids;
      ObjectType = objectType;
      CreatedBy = createdBy;
    }
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void constructor_with_null_ids_throw() {
    fixture.Register<ObjectId[]>(() => null);
    fixture.CreateAnonymous<CreateObject>();
}

Ответ 1

IMO, Комментарий Ruben Bartelink - лучший ответ.

С AutoFixture.Idioms вы можете сделать это вместо:

var fixture = new Fixture();
var assertion = new GuardClauseAssertion(fixture);
assertion.Verify(typeof(CreateObject).GetConstructors());

Метод Verify предоставит вам довольно подробное сообщение об исключении, если в аргументе конструктора в любом конструкторе отсутствует предложение Guard.


FWIW, AutoFixture широко использует Reflection, поэтому я не считаю его ошибкой, что она выбрасывает TargetInvocationException. Хотя он может разворачивать все экземпляры TargetInvocationException и восстанавливать их свойства InnerException, это также означает удаление (потенциально) ценной информации (например, трассировки стека AutoFixture). Я подумал об этом, но не хочу брать AutoFixture в этом направлении, именно по этой причине. Клиент всегда может отфильтровать информацию, но если информация удаляется преждевременно, ни один клиент не может вернуть ее.

Если вы предпочитаете другой подход, не слишком сложно написать вспомогательный метод, который разворачивает исключение - возможно, что-то вроде этого:

public Exception Unwrap(this Exception e)
{
    var tie = e as TargetInvocationException;
    if (tie != null)
        return tie.InnerException;
    return e;
}

Ответ 2

Я столкнулся с этим, когда искал что-то подобное. Я хотел бы добавить, что в сочетании с automoqcustomization и xunit ниже код также работает и намного чище.

    [Theory, AutoMoqData]
    public void Constructor_GuardClausesArePresent(GuardClauseAssertion assertion)
    {
        assertion.Verify(typeof(foo).GetConstructors());
    }

Вам просто нужно создать атрибут AutoMoqData следующим образом.

    public class AutoMoqDataAttribute : AutoDataAttribute
    {
        public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization()))
        {

        }
    }