2017-06-26 15 views
1

我想執行單元測試寫入的方法,但我面臨的問題。統一和NUnit

這裏是我UnityConfig類:

public class UnityConfig 
{ 
    #region Unity Container 
    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() => 
    { 
     var container = new UnityContainer(); 
     RegisterTypes(container); 
     return container; 
    }); 

    public static IUnityContainer GetConfiguredContainer() 
    { 
     return container.Value; 
    } 
    #endregion 

    public static void RegisterTypes(IUnityContainer container) 
    { 
     container.RegisterInstance(AutoMapperConfig.Initialize()); 
     container.RegisterType<IActiveDirectoryUserService, ActiveDirectoryUserService>(); 
     container.RegisterType<IUserRepository, InMemoryUserRepository>(); 
     container.RegisterType<IUserService, UserService>(); 
    } 
} 

測試方法:

public async Task RegisterAsync(string name, string email, string firstName, string lastName, string password) 
{ 
    var user = await _userRepository.GetBynameAsync(name); 

    if (user != null) 
    { 
     throw new Exception($"User {name} already exists."); 
    } 
    else 
    { 
     var ActiveDirectoryUser = await _ActiveDirectoryUserService.GetBynameAsync(name); 

     if (ActiveDirectoryUser == null) 
     { 
      throw new Exception($"User {name} does not exist in Active Directory"); 
     } 
     else 
     { 
      var salt = new Guid().ToString("N"); 
      user = new User(name, ActiveDirectoryUser.Email, ActiveDirectoryUser.FirstName, ActiveDirectoryUser.LastName, password, salt); 
      await _userRepository.AddAsync(user); 
     } 
    } 
} 

測試方法:

[Test] 
public async Task Test() 
{ 
    var userRepositoryMock = new Mock<IUserRepository>(); 
    var activeDirectoryUserMock = new Mock<IactiveDirectoryUserService>(); 
    var mapperMock = new Mock<IMapper>(); 

    var userService = new UserService(userRepositoryMock.Object, activeDirectoryUserMock.Object, mapperMock.Object); 

    await userService.RegisterAsync("name", "[email protected]", "first name", "last name", "password"); 

    userRepositoryMock.Verify(x => x.AddAsync(It.IsAny<User>()), Times.Once); 
} 

測試方法,工作正常,外測試,但是當我運行測試失敗。我在調試模式下運行測試,發現它並沒有進入

var user = await _userRepository.GetBynameAsync(name); 

var ActiveDirectoryUser = await _ActiveDirectoryUserService.GetBynameAsync(name); 

任何想法爲什麼它不會在測試過程中進入這些方法?我是否需要向UnityConfig添加一些條目以使其工作?

- 編輯

private static ISet<User> _users = new HashSet<User> 
{ 
    new User("user1", "[email protected]", "firstnam1", "lastname1", "pass", "salt"), 
    new User("user2", "[email protected]", "firstnam2", "lastname2", "pass", "salt"), 
    new User("user3", "[email protected]", "firstnam3", "lastname3", "pass", "salt") 
}; 

public async Task<IEnumerable<User>> GetAllAsync() 
    => await Task.FromResult(_users); 

回答

2

正在發生的事情是,模擬並不知道如何從一個異步調用返回所以當代碼試圖等待他們失敗。它不一定與你的DI容器有關。它不在這個單元測試中。

做出一些假設,因爲沒有提供關於測試中使用的類型的足夠信息。

鑑於測試方法,讓我們假設你有接口,這樣

public interface IUserRepository { 
    Task<User> GetByNameAsync(string name); 
    Task AddAsync(User user); 
} 
public interface IActiveDirectoryUserService { 
    Task<AdUserAccount> GetByNameAsync(string name); 
} 
public class AdUserAccount { 
    public string Email { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

注意,返回Task方法。

您需要設置模擬以瞭解如何處理異步調用並允許任務完成。

[Test] 
public async Task Test() { 
    //Arrrange 
    var name = "name"; 
    var email = "[email protected]"; 
    var firstname = "first name"; 
    var lastname = "last name"; 

    var userRepositoryMock = new Mock<IUserRepository>(); 
    //Testing that it returns no user for that name so return null from async call 
    userRepositoryMock.Setup(_ => _.GetByNameAsync(name)).ReturnsAsync((User)null); 
    //Need this to make sure async call runs to completion 
    userRepositoryMock.Setup(_ => _.AddAsync(It.IsAny<User>())).Returns(Task.FromResult((object)null)); 

    var activeDirectoryUserMock = new Mock<IActiveDirectoryUserService>(); 
    //fake account for mock AD call 
    var adUser = new AdUserAccount { 
     Email = email, 
     FirstName = firstname, 
     LastName = lastname 
    };    
    //Need fake ad account to return from async call 
    activeDirectoryUserMock.Setup(_ => _.GetByNameAsync(name)).ReturnsAsync(adUser); 

    var mapperMock = new Mock<IMapper>(); 

    var userService = new UserService(userRepositoryMock.Object, activeDirectoryUserMock.Object, mapperMock.Object); 

    //Act 
    await userService.RegisterAsync(name, email, firstname, lastname, "password"); 

    //Assert 
    userRepositoryMock.Verify(x => x.AddAsync(It.IsAny<User>()), Times.Once); 
} 
+0

謝謝你的回答。您假設的接口看起來與薄荷一樣。我不明白兩件事。對於userRepositoryMock,你爲什麼要調用AddAsync方法?也爲adUserMock爲什麼你創建了假帳戶?有沒有其他的庫支持異步調用,所以我可以用更溫和的方式編寫測試? – ironcurtain

+0

@ironcurtain'AddAsync'返回一個'Task'。如果你不設置它,模擬將不會返回任何東西,並且被測試的方法不會流向完成。 – Nkosi

+0

@ironcurtain同樣適用於其他依賴項。它沒有正確設置。在測試中,依賴性被模擬,以便被測試的方法可以被隔離地測試,而沒有任何由於使用具體實現而可能出現的敲打效應。正在測試的方法也是異步的,因此安排測試時需要考慮這些方法。 – Nkosi