如何在Unity中处理一个IDisposable仓库?

6

我有一个工作,其中使用了IDisposable DbContext。我希望在不使用数据库的情况下对这个工作进行单元测试。我有哪些选项可以实现这一点?

我正在使用微软的默认 Fakes 组件。

我的工作:

public void Work()
{
    do
    {   
        //code here                
        using (var repository = new Repository<User>())
        {
            repository.Save(user);
        }

    } while (true);
}

我是一个有用的助手,可以为您进行翻译。以下是您需要翻译的内容:

我正在尝试进行测试,在测试的这个部分失败了,因为它实际上创建了 Repository 类的一个新实例。

我的测试方法:

using (ShimsContext.Create())
{
    Data.Repository.Fakes.ShimRepository<Domain.Fakes.ShimUser>.Constructor = (a) => { };

    Data.Repository.Fakes.ShimRepository<Domain.Fakes.ShimUser>.AllInstances.SaveT0 = (a, b) =>
    {
    };

   var service = GetService();
    service.Work(); //Throws exception
}

我该如何伪造这个“保存”方法?

1
看到依赖注入 - 你需要允许传递一个参数到Work()函数中,或者至少传递一个服务,否则它总是会创建一个真实的对象。 - Alex Krupka
我不熟悉Fakes,但也许它并不完全满足您的需求。我知道使用像Moq(https://github.com/Moq/moq4)这样的库可以轻松实现此功能。 - Chris Pratt
这也不是使用Moq轻而易举的事情,因为他在Work中实例化了一个实际的具体存储库实例。 - Derek Van Cuyk
使用 MS Fakes 或 TypeMock 等工具应该是最后的选择。如果可以,将代码抽象成接口,除非你真的无法控制代码,在这种情况下,你应该使用一种模拟框架。 - Jason Evans
是的,我明白这违反了依赖倒置原则,但问题在于每个循环都必须处理掉这个上下文。 - gog
2个回答

7
你在这里违反了DIP,使得单元测试你的服务比它应该更难。你也应该避免使用通用的存储库,而是使用角色接口
相反,向你的服务注入一个抽象的存储库,例如IUsersRepository,它定义了你的Save方法。然后在你的服务单元测试中,你可以简单地使用IUsersRepository的桩实现。

是的,但对象的处理怎么样呢?如果我只注入一次,作业将永远使用上下文的第一个实例运行。 - gog
处理是实体框架抽象实现的一个实现细节。因此,处理是在例如EntityFrameworkUsersRepository中完成的。 - devdigital

2

伪代码往往会揭示您的代码没有正确遵循SOLID中的D,因为您在类内部创建依赖项而不是将它们传递进去。

一个更好的模式是创建一个ISaveRepository接口,该接口实现IDisposable并公开一个Save()方法。然后,您应该将存储库的实例注入到您的类中。这将使您能够满足使用语句测试,并实现定义了不会访问数据库的.Save()方法的模拟。

public class Test
{
    private readonly ISaveRepository _userRepository;

    public Test(ISaveRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public void Work()
    {
        using (_userRepository)
        {
            var cont = true;
            do
            {              
                _userRepository.Save(new User());
                cont = false;
            } while (cont);
        } 
    }
}

public interface ISaveRepository : IDisposable
{
    void Save<T>(T model);
}

public class Repository<T> : ISaveRepository
{
    public void Dispose() { }
    public void Save<TT>(TT model) {}
}

public class User {}

你的存根实现需要dispose吗?XML存储库需要dispose吗?Web服务调用存储库需要dispose吗?等等。 - devdigital
@ggui 我猜你最初重新创建连接是为了避免长时间保持连接,是吗? - David L

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接