TDD - 想用假存储库测试我的服务层,但是如何?

Ale*_*lex 13 c# asp.net asp.net-mvc repository-pattern

我设计了一个使用存储库模式的应用程序,然后设计了一个单独的服务层,例如:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        IRepository<User> userRepository = new UserRepository();
        // add user, etc
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我在Register方法中实例化我的存储库.现在,因为我想编写一些单元测试,我真的无法实现它并用虚假的存储库替换它可以吗?

我不想将存储库添加为类变量(并通过构造函数设置),因为我认为这会使我的代码"臭"(并非所有方法都需要所有存储库,而且我不需要调用层来了解存储库等).

建议?

jri*_*sta 16

您需要使用依赖注入.UserRepository是RegistrationService类的依赖项.为了使您的类适当地单元可测试(即隔离它们的依赖项),您需要"反转"控制依赖项创建的内容.目前,您可以直接控制,并在内部创建它们.只需反转该控件,并允许外部的东西(如像Castle Windsor这样的IoC容器)注入它们:

public class RegistrationService: IRegistrationService
{
    public RegistrationService(IRepository<User> userRepo)
    {
        m_userRepo = userRepo;
    }

    private IRepository<User> m_userRepo;

    public void Register(User user)
    {
        // add user, etc with m_userRepo
    }
}
Run Code Online (Sandbox Code Playgroud)

我想我忘了添加.一旦允许注入依赖项,就可以打开它们可模拟的可能性.由于您的RegistrationService现在将其依赖用户存储库作为其构造函数的输入,因此您可以创建模拟存储库并将其传递,而不是实际存储库.


Jos*_*eph 9

依赖注入对于这些事情来说非常方便:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        this(user, new UserRepository());
    }

    public void Register(User user, IRepository<User> userRepository)
    {
        // add user, etc
    }

}
Run Code Online (Sandbox Code Playgroud)

这样您就可以使用UserRepository作为默认设置,并为您需要的任何其他内容传递自定义(Mock或Stub进行测试)IRepository.您可能还想更改第二个寄存器方法的范围,以防您不想将其暴露给所有内容.

一个更好的选择是使用一个IoC容器,它会为你购买比上面例子更加分离的语法.你可以得到这样的东西:

public class RegistrationService: IRegistrationService
{
    public void Register(User user)
    {
        this(user, IoC.Resolve<IRepository<User>>());
    }

    public void Register(User user, IRepository<User> userRepository)
    {
        // add user, etc
    }

}
Run Code Online (Sandbox Code Playgroud)

有许多IoC容器,如其他人的答案所列,或者你可以自己推出.