如何使用在方法中具有依赖关系的moq对类进行单元测试?

kuz*_*agi 1 c# unit-testing moq

我想对一个用于验证用户的类AuthClient(使用Moq)进行单元测试.

问题是我的AuthenticationService依赖关系没有注入课堂.我对如何在不注入依赖项的情况下测试流程感到困惑.

[TestClass] 
public class AuthClient 
{
    string Dictionary<string, IList<UserPermissions> _userPermissions;

    User Login(string username, string password)
    {
        IAuthenticationService authenticationService = new AuthenticationService();
        User user = authService.Login(username, password);

        IAuthorizationService authorizationService = new AuthorizationService();
        var permissions = authorizationService.GetPermissions(user);
        _userPermissions.Add(user.Username, permissions);

        return user;
    }
}
Run Code Online (Sandbox Code Playgroud)

我目前的单元测试代码如下:

public class UnitTestClass
{
    public string UserName {get;set;}
    public string Password {get;set;}

    [TestInitialize]
    public void Setup() 
    {
        UserName = "test";
        Password = "test123";
    }

    [TestMethod]
    public void When_ValidUserCredentials_Expect_UserIsNotNull()
    {
        var mockAuthenticationService = new Mock<IAuthenticateService>();
        mockAuthenticationService.Setup(m => m.Login(ValidUserName, ValidPassword)).Returns(ValidUserDto);

        var mockAuthorizationService = new Mock<IAuthorizeService>();
        mockAuthorizationService.Setup(m => m.GetAllPermissions(ValidUserId)).Returns(ValidPermissionList);

        var authClient = new Mock<AuthClient>();
        // I want to use mock dependencies here to test flow against mock data
        User user = authClient.Login(UserName, Password); 

        Assert.IsNotNull(user);
    }
}
Run Code Online (Sandbox Code Playgroud)

一切都出错的想法?

ry8*_*806 8

Moq允许您测试依赖于其他类/对象的类(以及许多其他内容).注意:Moq不是IOC容器.

在你的AuthClient类中,你不允许给它任何依赖,而是它实例化它自己的例如

IAuthenticationService authenticationService = new AuthenticationService();
Run Code Online (Sandbox Code Playgroud)

相反,你需要在AuthClient类上定义一个构造函数,它接受两个对象(它应该依赖它),还有其他方法来"注入"依赖项,但是现在我们将使用构造函数注入(很好的例子)

例如

public class AuthClient 
{
    string Dictionary<string, IList<UserPermissions> _userPermissions;
    private IAuthenticationService _authenticationService;
    private IAuthorizationService _authorizationService;

    public AuthClient(IAuthenticationService authenticationService, IAuthorizationService authorizationService)
    {
        _authenticationService = authenticationService;
        _authorizationService = authorizationService;
    }

    User Login(string username, string password)
    {
        User user = _authenticationService.Login(username, password);

        var permissions = _authorizationService.GetPermissions(user);
        _userPermissions.Add(user.Username, permissions);

        return user;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,每次要使用此类时,都必须告诉它应该使用哪种授权服务和身份验证服务.您已将AuthClient与特定实现解耦 - 现在它非常可测试且可重用(如果您想创建一个新的AuthenticationService,它执行与旧的完全不同的操作,您只需编写一个实现IAuthenticationService的类!)

现在,每次要使用它时,都必须包含AuthClient的依赖项(将其提供给它的构造函数).你应该阅读更多关于依赖注入和IOC(控制反转)(这是一个非常大的主题,有很多不同的方法来做同样的事情),谷歌是你的朋友在这里.

测试类现在应该如下所示:

public class UnitTestClass
{
    public string UserName {get;set;}
    public string Password {get;set;}

    [TestInitialize]
    public void Setup() 
    {
        UserName = "test";
        Password = "test123";
    }

    [TestMethod]
    public void When_ValidUserCredentials_Expect_UserIsNotNull()
    {
        var mockAuthenticationService = new Mock<IAuthenticateService>();
        mockAuthenticationService.Setup(m => m.Login(ValidUserName, ValidPassword)).Returns(ValidUserDto);

        var mockAuthorizationService = new Mock<IAuthorizeService>();
        mockAuthorizationService.Setup(m => m.GetAllPermissions(ValidUserId)).Returns(ValidPermissionList);

        // Here we are "injecting" the dependencies into the AuthClient
        var authClient = new AuthClient(mockAuthenticationService.Object, mockAuthorizationService.Object);
        // This service call will now use the supplied (mocked) services above
        User user = authClient.Login(UserName, Password); 

        Assert.IsNotNull(user);
    }
}
Run Code Online (Sandbox Code Playgroud)