简体   繁体   English

我如何使用 Moq 或 NInject Mocking Kernel 模拟接口

[英]How do i mock a Interface with Moq or NInject Mocking Kernel

I just waded through questions and blogs on the subject of mocking and Dependency Injection.我只是浏览了有关模拟和依赖注入主题的问题和博客。 Come to a conclusion i just need to mock the interface that is consumed by client.得出一个结论,我只需要模拟客户端使用的接口。 I am looking forward to testing a simple use case here with no idea.我期待在这里测试一个简单的用例,但不知道。

The Contract合同

public Interface IApplicationService
{

     bool DeleteApplication(int id);

     ApplicationDto AddApplication(ApplicationDto application);

     IEnumerable<ApplicationDto> GetApplications();

}

Implementation ( I am going to mock )实现(我要模拟)

public Class ApplicationService:IApplicationService
{
    private EntityFrameworkRepo repo; 

    public ApplicationService()
    {
         repo = new EntityFrameworkRepo();
    }

    public ApplicationDto Add(ApplicationDto dto)
    {
         //add to dbcontext and commit
    }

}

Mocking Code模拟代码

[Test(Description = "Test If can successfully add application")]
public void CanAddApplication()
{
    //create a mock application service
    var applicationService = new Mock<IApplicationService>();

    //create a mock Application Service to be used by business logic
    var applicationDto = new Mock<ApplicationDto>();

    //How do i set this up
    applicationService.Setup(x => x.GetApplications()).Returns(IEnumerable<applicationDto.Object>);
}

And i for one am sure i need to test the business logic rather than mocking it.并且我确信我需要测试业务逻辑而不是嘲笑它。 So what is it exactly i have to do to test my ApplicationService but then keep the entity framework out.那么我到底需要做什么来测试我的ApplicationService但然后将实体框架排除在外。

btw to speak of ApplicationService , it uses constructor injection with NInject.顺便说一下ApplicationService ,它使用带有 NInject 的构造函数注入。 So mocking this with NInject.MockingKernel will setup dependency chain?所以用NInject.MockingKernel这个会设置依赖链吗?

There is little or no benefit using dependency injection (IOC) container in unit testing.在单元测试中使用依赖注入 (IOC) 容器几乎没有好处。 Dependency injection helps you in creating loose coupled components, and loose coupled components are easier to test, thats it.依赖注入帮助你创建松耦合组件,松耦合组件更容易测试,就是这样。

So if you want to test some service, just create mockups of it dependencies and pass them to that service as usual (no need to involve IOC container here, I hardly can imagine, that you will need some features of IOC containers - like contextual binding, interception etc. - inside unit test).因此,如果您想测试某些服务,只需创建它的依赖项的模型并将它们照常传递给该服务(这里不需要涉及 IOC 容器,我几乎无法想象,您将需要 IOC 容器的一些功能 - 例如上下文绑定,拦截等 - 内部单元测试)。

If you want your ApplicationService to be easy to test, it should look more like:如果您希望ApplicationService易于测试,它应该看起来更像:

public class ApplicationService: IApplicationService
{
    private readonly IEntityFrameworkRepo repo; 

    // dependency passed by constructor
    public ApplicationService(IEntityFrameworkRepo repo)
    {
         this.repo = repo;
    }

    // save to db when DTO is eligible
    public ApplicationDto Add(ApplicationDto dto)
    {
         // some business rule
         if(dto.Id > 0 && dto.Name.Contains(string.Empty)){
              //add to dbcontext and commit
         }else{
              throw new NotEligibleException();
         } 
    }   
}

Here the dependency is passed by constructor.这里的依赖是由构造函数传递的。 In your application code you will use it together with an IOC container to make constructor injection (IOC container will be responsible for creating instances of IEntityFrameworkRepo ).在您的应用程序代码中,您将使用它与 IOC 容器一起进行构造函数注入(IOC 容器将负责创建IEntityFrameworkRepo实例)。

But in unit test, you can just pass instance of some implementation of IEntityFrameworkRepo created on your own.但是在单元测试中,您可以只传递自己创建的IEntityFrameworkRepo的某些实现的实例。

ApplicationDto

As long as ApplicationDto is some object that can by created by hand, I can directly use it in unit-test (creating instances by hand).只要ApplicationDto是一些可以手工创建的对象,我就可以直接在单元测试中使用它(手工创建实例)。 Otherwise I will have to wrap it by interface like IApplicationDto , in order to be able to mock it up with Moq.否则我将不得不通过像IApplicationDto这样的接口来包装它,以便能够用 Moq 模拟它。

public class ApplicationDto{
     public int Id {get; set;}
     public string Name {get; set;}
}

Here is how could unit-test look like:这是单元测试的样子:

In unit test I will use mocked implementaion of IApplicationRepo , because I do not want to configure eg database connections, web services etc. and my primary intention is to test the ApplicationService not the underlying repository.在单元测试中,我将使用IApplicationRepo实现,因为我不想配置例如数据库连接、Web 服务等,我的主要目的是测试ApplicationService而不是底层存储库。 Another advantage is that the test will be runnable without specific configuration for various machines.另一个优点是无需为各种机器进行特定配置即可运行测试。 To mockup some db repository I can use eg List .为了模拟一些数据库存储库,我可以使用例如List

[Test(Description = "Test If can successfully add application")]
public void CanAddApplicationIfEligible()
{
    var repo = GetRepo();
    var appService = new ApplicationService(repo);       
    var testAppDto = new ApplicationDto() { Id = 155, Name = "My Name" };

    var currentItems = repo.ApplicationDtos.Count();

    appService.Add(testAppDto);

    Assert.AreEqual(currentItems + 1, repo.ApplicationDtos.Count());
    var justAdded = repo.ApplicationsDto.Where(x=> x.Id = 155).FirstOrDefault();
    Assert.IsNotNull(justAdded);
    ///....
}

private static IEntityFrameworkRepo GetRepo{
    // create a mock repository
    var listRepo = new List<ApplicationDto>{
                         new ApplicationDto {Id=1, Name="MyName"}               
                   };

    var repo = new Mock<IEntityFrameworkRepo>();

    // setup the methods you know you will need for testing

    // returns initialzed list instead of DB queryable like in real impl.
    repo.Setup(x => x.ApplicationDtos)
        .Returns<IQueryable<ApplicationDto>>(x=> listRepo);

    // adds an instance of ApplicationDto to list
    repo.Setup(x => x.Add(It.IsAny<ApplicationDto>())
        .Callback<ApplicationDto>(a=> listRepo.Add(a));
    return repo.Object;
}

Note:笔记:

There have been realeased an ninject.mockingkernel extension.已经发布了ninject.mockingkernel扩展。 The approach described in example on wiki can make your unit-test code bit tidier, but the approach described there is definetly not depencdency injection (it is service locator). wiki 上示例中描述的方法可以使您的单元测试代码更整洁,但是那里描述的方法绝对不是依赖注入(它是服务定位器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM