2017-08-21 73 views
0

我做了重构,如果我们的仓库工厂,使之更加通用的,现在建立资料库的方法看起来像这样:嘲讽通用仓库工厂方法

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需要具体类型。

+1

不直接相关,但在前5行代码中存在Factory,存储库,通用性,参数数组和ServiceProvider可能是过度工程的标志。 – guillaume31

回答

0

其实我找到了解决这个问题的方法,我现在比较高兴,但如果有其他建议出现,我很乐意听到他们。

所以问题是,我的CreateRepository返回具体实现,而它必须返回抽象,因为我所有的嘲笑都是抽象(因为Moq嘲笑抽象)。因此,我修改了我的方法如下:

TInterface CreateRepository<TRepository, TInterface>(params object[] parameters) 
    where TRepository : class, TInterface where TInterface : class; 

而这保证了我的设置方法和成功后的编译时安全。我确实觉得有点麻烦,但现在我可以忍受它。

注:你总是可以做内部类中扩展方法在检测装置和复制粘贴&从生产方法的代码,这样你就不会污染你的产品代码,但是,这可能是,如果有人有问题试图在不改变测试方法的情况下改变生产方法,这就是为什么为了安全起见,我选择“污染”我的生产代码的原因。

1

我觉得你的问题是,你所期望的模仿对象:

protected readonly Mock<IConcreteRepository> _concreteRepositoryMock = new Mock<IConcreteRepository>(); 

要ConcreteRepository,这是错误的假设的实例。

_concreteRepositoryMock.Object is IConcreteRepository 

这应该说是 “真”,而

_concreteRepositoryMock.Object is ConcreteRepository 

这应该说是 “假”。

您必须要么在你模拟切换您PROD代码很乐意与抽象(IConcreteRepository)或嘲笑的最后一类

protected readonly Mock<ConcreteRepository> _concreteRepositoryMock = new Mock<ConcreteRepository>(); 

(注意,嘲弄具体类通常是不容易的 - 你需要的所有嘲笑方法是虚拟的等)。