Может ли NSubstitute имитировать возвращаемое значение, основываясь на аргументе интерфейса, а не на конкретном типе?
У меня есть сущность и валидатор:
public class Customer : IEntity { /* ... */ }
public class CustomerValidator : IValidator<Customer> { /* ... */ }
Я хочу издеваться над фабрикой валидаторов:
public interface IValidatorFactory
{
IValidator<TEntity> create<TEntity>(TEntity entity) where TEntity : class, IEntity;
}
Мой макет:
var mockFactory = Substitute.For<IValidatorFactory>();
mockFactory.create(Arg.Any<Customer>()).Returns(m => new CustomerValidator());
Это работает, когда аргумент Customer
,
Но код, который я тестирую, проходит IEntity
, так что макет не работает.
Могу ли я переписать макет для обработки аргумента, типизированного для интерфейса, а не для конкретного класса?
1 ответ
Вы имеете дело с обобщениями, которые по умолчанию не сохраняют наследование параметров типа. Параметры универсального типа по умолчанию не изменяются. Это означает, что вы не можете вернуть экземпляр IValidator<Customer>
как IValidator<IEntity>
, даже если Customer
происходит от IEntity
, Вам нужно явно указать отношение контравариантности, которое позволит вам использовать более общий тип IValidator<IEntity>
чем изначально создан IValidator<Customer>
, Просто пометьте параметр типа с помощью out
ключевое слово:
public interface IValidator<out T> where T : IEntity { }
Вы можете прочитать о "Ковариантности и Контравариантности в Обобщениях"
Вот полный пример:
public interface IValidatorFactory
{
IValidator<TEntity> create<TEntity>(TEntity entity) where TEntity : class, IEntity;
}
public interface IEntity { }
public interface IValidator<out T> { }
public class Customer : IEntity { }
public class CustomerValidator : IValidator<Customer> { }
[Test]
public void SO_43360005()
{
var sub = Substitute.For<IValidatorFactory>();
sub.create(Arg.Is<IEntity>(x => x is Customer)).Returns(_ => new CustomerValidator());
}