繁体   English   中英

如何对该ASP.NET Web App Controller进行单元测试?

[英]How to unit test this ASP.NET Web App Controller?

我有一个ASP.NET Web应用程序,想为主控制器编写一些UT。

大多数控制器方法是与UI的交互。 例如,每当加载主页时,它就会调用控制器以通过API调用从数据库中获取所有用户。

该方法是控制器中获取所有用户的方法。

public JsonResult GetAllUsers()
{
    List<User> users = null;
    try
    {
        users = new List<User>();
        var allUsersJsonResults = Requests.GetFromUri(Settings.AllUsersUri);
        users = JsonConvert.DeserializeObject<List<User>>(allUsersJsonResults.ToString());

        return Json(new
        {
            usersDetails = users,
            errorMessage = ""
        }, JsonRequestBehavior.AllowGet);
    }
    catch (Exception ex)
    {
        return Json(new
        {
            usersDetails = users,
            errorMessage = "Error loading users"
        }, JsonRequestBehavior.AllowGet);
    }
}

这将是实际调用API的方法

public static string GetFromUri(string fullUri)
{
    HttpClientHandler handler = new HttpClientHandler
    {
        Credentials = Settings.ServiceCredential
    };

    using (HttpClient client = new HttpClient(handler))
    {
        return client.GetStringAsync(fullUri).Result;
    }
}

最后,这是User类的样子

public class User
{
    public int UserId {get; set;}
    public string UserName {get; set;}
    public string UserLastName {get; set;}
    public int UserAge {get; set;}
    public int UserNationality {get; set;}
}

我现在想做的是编写一些单元测试来验证GetAllUsers()调用,但是我不确定该怎么做。 我认为我不应该调用Test或Production API调用,因为它们可能会不时返回完全不同的数据并使单元测试失败,但是我不确定如何1)在不调用服务器API的情况下测试控制器和2 )模拟一些数据以模拟答案。

GetFromUri静态方法。 结果,很难对单元测试进行模拟。

理想情况下,我们要将Interface注入Controller

调节器

我们将需要返回强类型的类UserResponse而不是匿名类型,以便我们可以在单元测试中将其转换为原始类型。

public class UsersController : Controller
{
    private readonly IRequests _requests;

    public UsersController(IRequests requests)
    {
        _requests = requests;
    }

    public JsonResult GetAllUsers()
    {
        List<User> users = null;
        try
        {
            users = new List<User>();
            var allUsersJsonResults = _requests.GetFromUri(Settings.AllUsersUri);
            users = JsonConvert.DeserializeObject<List<User>>(allUsersJsonResults);

            return Json(new UserResponse { usersDetails = users, errorMessage = "" },
                JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            return Json(users, JsonRequestBehavior.AllowGet);
        }
    }
}

IRequests和请求

仅供参考:请重命名这些班级名称。 类名应为单数名词。

public interface IRequests
{
    string GetFromUri(string fullUri);
}

public class Requests : IRequests
{
    public string GetFromUri(string fullUri)
    {
        HttpClientHandler handler = new HttpClientHandler
        {
            Credentials = Settings.ServiceCredential
        };

        using (HttpClient client = new HttpClient(handler))
        {
            return client.GetStringAsync(fullUri).Result;
        }
    }
}

其他班级

public class Settings
{
    public static ICredentials ServiceCredential { get; set; }
    public static string AllUsersUri { get; set; }
}

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserResponse
{
    public IList<User> usersDetails { get; set; }
    public string errorMessage { get; set; }
}

单元测试使用

我将NUnit与NSubstitute一起使用。 您可以轻松地替换为任何您喜欢的模拟框架。

[TestFixture]
public class UserControllerTest
{
    private User _user1;
    private User _user2;
    private IList<User> _users;

    [SetUp]
    public void SetUp()
    {
        _user1 = new User { FirstName = "John", LastName = "Doe" };
        _user2 = new User { FirstName = "Janet", LastName = "Doe" };
        _users = new List<User> { _user1, _user2 };
    }

    [Test]
    public void GetAllUsers_ReturnUsers()
    {
        // Arrange
        var requests = Substitute.For<IRequests>();
        requests.GetFromUri(Arg.Any<string>()).Returns(JsonConvert.SerializeObject(_users));

        var sut = new UsersController(requests);

        // Act
        var result = sut.GetAllUsers() as JsonResult;

        // Assert
        Assert.IsNotNull(result);
        Assert.IsNotNull(result.Data);
        var users = result.Data as UserResponse;
        Assert.AreEqual(users.usersDetails[0].FirstName, _users[0].FirstName);
        Assert.AreEqual(users.usersDetails[1].FirstName, _users[1].FirstName);
    }
}

动作以及扩展控制器与实现问题紧密联系在一起。 尝试保持控制器倾斜。 在接口依赖项之后抽象实现,以便可以单独对其进行模拟和测试。

当前设计中的动作与静态依赖关系紧密耦合,这使得测试变得困难。

使用以下

public interface IUsersService {
    GetAllUsersResponse GetAllUsers();
}

public class GetAllUsersResponse {
    public IList<User> usersDetails { get; set; }
    public string errorMessage { get; set; }
}

这是用抽象重构的控制器动作的示例。

public class MainController : Controller {
    private IUsersService usersService;

    public MainController(IUsersService service) {
        this.usersService = service;
    }

    public JsonResult GetAllUsers() {
        var response = usersService.GetAllUsers();
        return Json(response, JsonRequestBehavior.AllowGet);
    }
}

并对该动作进行更简单的单元测试。 注意:使用Moq来模拟依赖项

[TestMethod]
public void GetAllUsers_Should_Call_Service() {
    //Arrange
    var mock = new Mock<IUsersService>();
    var response = new GetAllUsersResponse {
        usersDetails = new List<User>(),
        errorMessage = string.Empty,
    };
    mock.Setup(m => m.GetAllUsers()).Returns(response);
    var controller = new MainController(mock.Object);
    //Act
    var jsonResult = controller.GetAllUsers();
    //Assert
    mock.Verify(m => m.GetAllUsers(), Times.AtLeastOnce());
}

尽管当前动作失败,但可以将其封装在抽象后面。

暂无
暂无

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

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