Ilyes Soussi
Ilyes Soussi

Reputation: 17

Can someone tell me how to test this method

I am an absolute beginner to testing and .net and I need to test this method as soon as possible for a school project.

This is the code :

// GET: Books/Archive
public async Task<ActionResult> Archive()
{
    var usersBooks = await _bookManager.GetArchiveBooks(Guid.Parse(HttpContext.User.Identity.GetUserId()));

    var result = usersBooks.Select(ConvertArchiveBookToViewModel).ToList();

    return View(new ArchiveViewModel
        {
            Books = result
        });
}

Any answer would be really helpful and thank you :)

Upvotes: 0

Views: 80

Answers (1)

David
David

Reputation: 218960

First thing's first... You need to mock _bookManager as a dependency for this method.

Where does _bookManager come from? Presumably it's a class-level property. So it should provide some means by which to use a mock. You should likely use constructor injection, but if you're not familiar with wiring up dependencies in ASP.NET MVC then it might get a little too complex for now. An injectable property should work just as well. Something like this:

public class MyController
{
    private SomeType _bookManager;
    public SomeType BookManager
    {
        get { return _bookManager; }
        set { _bookManager = value; }
    }

    public async Task<ActionResult> Archive()
    {
        // your method
    }
}

Presumably also there is code elsewhere in that class which otherwise initializes _bookManager before using it. You're going to want to modify that logic a bit so that it doesn't overwrite any supplied mocks. One pattern that often works well for me is to use the property itself even internal to the class and to lazy-initialize in the property. Something like this:

public class MyController
{
    private SomeType _bookManager;
    public SomeType BookManager
    {
        get
        {
             if (_bookManager == null)
                 _bookManager = new SomeType();
             return _bookManager;
        }
        set { _bookManager = value; }
    }

    public async Task<ActionResult> Archive()
    {
        // IMPORTANT: Use "BookManager" instead of "_bookManager"
    }
}

The idea here is that if you supply a mock (or any dependency implementation) for the BookManager then the code would use that. Otherwise it would use whatever you're currently using.

Now that your class is set up for allowing the use of a mock, you need to create a mock. There are many mocking libraries available. Personally I use RhinoMocks.

The purpose of a mock is to provide expected, defined behavior. This is because...

You are testing Archive(). You are not testing BookManager.GetArchiveBooks()

Using the mocking library of your choice, in your test you would set up an instance of SomeType (or whatever your type is called, obviously) to return a defined and expected result from GetArchiveBooks(). Based on that result, you would predict the result of the method you're testing and validate that it produces exactly that result.

In a broad sense, your test would look something like this:

// arrange
var bookManager = MockRepository.GenerateMock<SomeType>();
// TODO: configure the object to return a known result from GetArchiveBooks()
var controller = new MyController();
controller.BookManager = bookManager;

// act
var result = await controller.Archive();

// assert
// TODO: inspect the result to ensure it contains what you expect

For the mocking library of your choice, take a look at some examples for setting up a "stub" for the method being called (in this case GetArchiveBooks()).

For inspecting the result, first you're going to want to step through this test in a debugger and see what result actually has. A view result has a lot of properties on it, and I don't know them off the top of my head. But if you inspect it in a debugger you should be able to find your model in one of those properties, as well as potentially other things you could potentially validate if you want to. (Depending on how many things you want to assert in this test.)

The goal here is to make sure that the returned model is exactly what you expect it to be based on the known behavior of the mocked dependency. If it is, then the method passes the test.


Edit: I just noticed a second dependency in the method:

HttpContext.User.Identity.GetUserId()

Modern ASP.NET MVC implementations may provide some helpful ways to mock HttpContext as well, though I'm not familiar with that off the top of my head either. Worst case scenario is that you just expose another injectable property to mock it. Something like this:

private string _userID;
public string UserID
{
    get
    {
        if (string.IsNullOrWhiteSpace(_userID))
            _userID = HttpContext.User.Identity.GetUserId();
        return _userID;
    }
    set { _userID = value; }
}

Then in your action method you would use that property instead of calling the HttpContext directly. And in your test, as part of the "arrange" step, you would supply a mock string. Which is pretty easy:

controller.UserID = "testUser";

As you can see at this point, testability is all about dependency management. Each individual testable piece of code should be isolated from any and all dependencies, no matter how small they may be. (Such as getting the user ID from HttpContext.) "Invert" those dependencies to allow code to supply the information rather than having your testable code be responsible for getting the information.

Upvotes: 3

Related Questions