0
votes

I have an ASP.NET Web Application and want to write some UT for the main controller.

Most of the controller methods are interactions with the UI. For instance, whenever the main page loads, it calls the controller to get all the users from the database through an API call.

This method is the one in the controller that get all the users.

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

This will be the method to do the actual call to the 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;
    }
}

Lastly, this is how the User class looks like

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

What I am trying to do at this point is to write some Unit Tests to validate GetAllUsers() call but I am not sure how to do that. I think I should not call neither the Test or the Production API calls since they could return completely different data from time to time and make the Unit Tests fail but I am not sure how to 1) Test the controller without calling the server API and 2) mock some data to simulate the answer.

2
Not sure what you're asking .. if you expect the external api to fail then there is nothing to do except for making an analysis test - Modar Na
What you are missing is wrapper around web api call. And make the comtroller depending on it by interface. Then you can use mocking to unit test this without calling actual web apis. - Chetan

2 Answers

3
votes

GetFromUri is a static method. As the result, it is not easy to mock for unit testing.

Ideally, we want to inject Interface into Controller.

Controller

We will need to return strongly type class UserResponse instead of anonymous, so that we can cast to the original type back in Unit Test.

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 and Requests

FYI: Please rename those class name. Class name should be singular noun.

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

Other Classes

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

Unit Test using

I use NUnit with NSubstitute. You can easily replace with any mock framework that you like.

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

The action and by extension the controller is too tightly coupled to implementation concerns. Try to keep the controller lean. Abstract the implementation behind an interface dependency so that it can be mocked and tested in isolation.

That action in its current design is tightly coupled to static dependencies which make it difficult to test.

Using the following

public interface IUsersService {
    GetAllUsersResponse GetAllUsers();
}

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

Here is an example of the controller action refactored with an abstraction.

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

And an even simpler unit test of that action. (Note: using Moq for mocking dependencies)

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

The current action despite all its failing can be encapsulated away behind an abstraction.