我已经对我们的存储库工厂进行了重构,使其更通用,现在创建存储库的方法看起来像这样:
public TRepository CreateRepository<TRepository>(params object[] parameters)
where TRepository : class
{
if (_serviceProvider == null)
throw new ArgumentNullException(nameof(_serviceProvider));
return ActivatorUtilities.CreateInstance<TRepository>(_serviceProvider, parameters);
}
在我的生产代码中,我正在使用它,它就像魅力一样:
_concreteRepo = repoFactory.CreateRepository<ConcreteRepo>();
但是当我试图重构单元测试时,我在设置工厂时遇到了困难,这就是我这样做的方法,但它不起作用。
public class Tests
{
// Since I am using Moq I can't mock anything but abstract types thus having problems with type conversion in set up.
protected readonly Mock<IConcreteRepository> _concreteRepositoryMock = new Mock<IConcreteRepository>();
protected readonly Mock<IRepositoryFactory> _factoryMock = new Mock<IRepositoryFactory>();
[SetUp]
public void SetUp()
{
// If I don't cast concreteRepositoryMock compiler complains that cannot convert from abstract to concrete repository.
// If I cast it fails and returns null.
_factoryMock.Setup(f => f.CreateRepository<ConcreteRepository>())
.Returns(_concreteRepositoryMock.Object as ConcreteRepository);
}
}
任何想法如何解决它?好像我的CreateRepository
方法正在返回具体类型,但是模仿我无法模拟我的具体存储库。我也无法将抽象类型传递给CreateRepository
,因为CreateInstance
需要具体类型。
答案 0 :(得分:1)
我认为你的问题是你期望Mock对象:
protected readonly Mock<IConcreteRepository> _concreteRepositoryMock = new Mock<IConcreteRepository>();
成为ConcreteRepository的实例,这是错误的假设。
_concreteRepositoryMock.Object is IConcreteRepository
时应该说“TRUE”
_concreteRepositoryMock.Object is ConcreteRepository
这应该说“FALSE”。
您必须切换PROD代码以满足抽象(IConcreteRepository)或模拟最终类
protected readonly Mock<ConcreteRepository> _concreteRepositoryMock = new Mock<ConcreteRepository>();
在你的模拟中(请注意,模拟具体类通常并不容易 - 你需要所有模拟的方法都是虚拟的等等。)。
答案 1 :(得分:0)
实际上我找到了解决方法,我现在比较开心,但如果出现任何其他建议,我很乐意听到。
问题是我的CreateRepository
返回具体实现,而它必须返回抽象,因为我的所有模拟都是抽象(因为Moq只模拟抽象)。因此,我修改了我的方法如下:
TInterface CreateRepository<TRepository, TInterface>(params object[] parameters)
where TRepository : class, TInterface where TInterface : class;
这保证了我的设置方法的编译时安全性以及之后的成功。我觉得它有点麻烦,但我现在可以忍受它。
注意:您可以随时在测试程序集的内部类中执行扩展方法,并复制并粘贴生产方法中的代码,这样您就不会污染生产代码,但是,如果有人试图改变生产方法而不改变测试方法,这可能会有问题,这就是为什么我选择&#34;污染&#34;我的生产代码是为了安全起见。