NSubstitute可以基于接口arg而不是具体类型来模拟返回值吗?

时间:2017-04-12 03:56:21

标签: .net unit-testing mocking 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,因此模拟不起作用。

我可以重写mock来处理输入到接口的参数而不是具体的类吗?

1 个答案:

答案 0 :(得分:2)

您正在处理泛型,默认情况下它们不“保留”类型参数继承。默认情况下,通用类型参数是不变的。这意味着您无法将IValidator<Customer>的实例作为IValidator<IEntity>返回,即使Customer来自IEntity。您需要明确指定逆变关系,使您能够使用比最初实例化的IValidator<IEntity>更通用的类型IValidator<Customer>。只需使用out关键字标记type参数:

public interface IValidator<out T> where T : IEntity { }

您可以在"Covariance and Contravariance in Generics"

中阅读

以下是完整的示例:

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