[英]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);
}
}
}
仅供参考:请重命名这些班级名称。 类名应为单数名词。
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.