簡體   English   中英

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

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

我只是瀏覽了有關模擬和依賴注入主題的問題和博客。 得出一個結論,我只需要模擬客戶端使用的接口。 我期待在這里測試一個簡單的用例,但不知道。

合同

public Interface IApplicationService
{

     bool DeleteApplication(int id);

     ApplicationDto AddApplication(ApplicationDto application);

     IEnumerable<ApplicationDto> GetApplications();

}

實現(我要模擬)

public Class ApplicationService:IApplicationService
{
    private EntityFrameworkRepo repo; 

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

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

}

模擬代碼

[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>);
}

並且我確信我需要測試業務邏輯而不是嘲笑它。 那么我到底需要做什么來測試我的ApplicationService但然后將實體框架排除在外。

順便說一下ApplicationService ,它使用帶有 NInject 的構造函數注入。 所以用NInject.MockingKernel這個會設置依賴鏈嗎?

在單元測試中使用依賴注入 (IOC) 容器幾乎沒有好處。 依賴注入幫助你創建松耦合組件,松耦合組件更容易測試,就是這樣。

因此,如果您想測試某些服務,只需創建它的依賴項的模型並將它們照常傳遞給該服務(這里不需要涉及 IOC 容器,我幾乎無法想象,您將需要 IOC 容器的一些功能 - 例如上下文綁定,攔截等 - 內部單元測試)。

如果您希望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();
         } 
    }   
}

這里的依賴是由構造函數傳遞的。 在您的應用程序代碼中,您將使用它與 IOC 容器一起進行構造函數注入(IOC 容器將負責創建IEntityFrameworkRepo實例)。

但是在單元測試中,您可以只傳遞自己創建的IEntityFrameworkRepo的某些實現的實例。

ApplicationDto

只要ApplicationDto是一些可以手工創建的對象,我就可以直接在單元測試中使用它(手工創建實例)。 否則我將不得不通過像IApplicationDto這樣的接口來包裝它,以便能夠用 Moq 模擬它。

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

這是單元測試的樣子:

在單元測試中,我將使用IApplicationRepo實現,因為我不想配置例如數據庫連接、Web 服務等,我的主要目的是測試ApplicationService而不是底層存儲庫。 另一個優點是無需為各種機器進行特定配置即可運行測試。 為了模擬一些數據庫存儲庫,我可以使用例如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;
}

筆記:

已經發布了ninject.mockingkernel擴展。 wiki 上示例中描述的方法可以使您的單元測試代碼更整潔,但是那里描述的方法絕對不是依賴注入(它是服務定位器)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM