jward01
jward01

Reputation: 759

Unit Test Mock Controller, C# Do I need to Mock HTTPContext? What methods do I mock?

I am tasked with writting unit Tests for some code we have in our Database. The Unit Tests must Mock everything, and test for both passing and failed scenarios. Currently I am using NUnit and FakeItEasy, I have used Moq in the past and don't mind using it again.

Controller

public class AccountController : BaseController
{
    private readonly IAccountsManager _accountsManager;
    private readonly ICallerInfoManager _callerInfoManager;

    public AccountController(IAccountsManager accountsManager, ICallerInfoManager callerInfoManager)
        : base(callerInfoManager)
    {
        _accountsManager = accountsManager;
        _callerInfoManager = callerInfoManager;
    }

    [HttpGet]
    [ActionName("GetAll")]
    public List<Account> Get()
    {
        var context = _callerInfoManager.GetFromHttpContext();
        return _accountsManager.GetAll(context.SiteLocationCode);
    }

    [HttpPost]
    [ActionName("Search")]
    public List<Account> Search(AccountRequest request)
    {
        var context = _callerInfoManager.GetFromHttpContext();
        return _accountsManager.GetAllWithNameContaining(request.SearchTerm, context.SiteLocationCode);
    }
}

CallerInfoManager

 public class CallerInfoManager : ICallerInfoManager
{
    private readonly IContactContextManager _contactContextManager;
    private const string ContactIdKey = "c";
    private const string SafeIdKey = "sa";
    private const string SiteLocationCode = "s";

    public CallerInfoManager(IContactContextManager contactContextManager)
    {
        _contactContextManager = contactContextManager;
    }

    public CallerInfo GetFrom(HttpRequest request)
    {
        return ExtractCallerInfo(request.QueryString);
    }

    public CallerInfo GetFromHttpContext()
    {
        return GetFrom(HttpContext.Current.Request);
    }

AccountManager

   public class AccountsManager : IAccountsManager
{
    private readonly IAccountRepository _accountRepository;

    public AccountsManager(IAccountRepository accountRepository)
    {
        _accountRepository = accountRepository;
    }

    public List<Account> GetAll(string siteLocationCode)
    {
        return _accountRepository.GetAll(siteLocationCode);
    }

    public List<Account> GetAllWithNameContaining(string term, string siteLocationCode)
    {
        return _accountRepository.Search(term, siteLocationCode);
    }

    public Account Add(Account account)
    {
        _accountRepository.Add(account);
        return account;
    }
}

This is what I have so far for my Unit tests. I really dont think I am doing it right. I feel like I am not properly mocking the objects.

Question: What methods am I supposed to be mocking and testing within the controller?


My Tests: (First one passes, second one isn't working)

    [TestFixture]

public class AccountControllerTests {

    //Tests that all accounts where returned
    [Test]
    public void GetAllAccounts_ReturnAllAccounts()
    {
        //Arrange
        var mockAccountsManager = A.Fake<IAccountsManager>();
        var mockCallerInfoManager = A.Fake<ICallerInfoManager>();

        using (var accountsController = new AccountController(mockAccountsManager, mockCallerInfoManager))
        {
            //Act
           List<Account> accounts = accountsController.Get();

            //Assert
           A.CallTo(() => mockCallerInfoManager.GetFromHttpContext()).MustHaveHappened();
           Assert.AreNotEqual(null, accounts); 
        }
    }

    //Tests that the proper search parameter was returned
    [Test]
    public void SearchforAccount_ReturnSearchAccount()
    {
        //Arrange
        var mockAccountsManager = A.Fake<IAccountsManager>();
        var mockCallerInfoManager = A.Fake<ICallerInfoManager>();

        Account searchAccountEntity = new Account
        {
            Id = 01,
            CompanyName = "google"

        };

        //Define search parameter
        AccountRequest mockAccountRequest = new AccountRequest
        {
            SearchTerm = "google"
        };

        using (var accountsController = new AccountController(mockAccountsManager, mockCallerInfoManager))
        {
            //Act
            List<Account> returnedAccounts = accountsController.Search(mockAccountRequest);
            mockAccountsManager.GetAllWithNameContaining("universal", "test");

            //Assert
            Assert.AreSame(mockAccountRequest, returnedAccounts);

        }
    }

Upvotes: 1

Views: 739

Answers (1)

NikolaiDante
NikolaiDante

Reputation: 18649

Question: What methods am I supposed to be mocking and testing within the controller?

This should probably be a question for the manager / team lead / architect / senior developer who set you the task :-)

second one isn't working

Is this instance the sut seems to me to be AccountController.Search but you're not mocking _accountsManager.GetAllWithNameContaining.

Also with Assert.AreSame(mockAccountRequest, returnedAccounts); one is a list, the other is an AccountRequest.

Try this:

    [Test]
    public void SearchforAccount_ReturnSearchAccount()
    {
        //Arrange
        var mockAccountsManager = A.Fake<IAccountsManager>();
        var mockCallerInfoManager = A.Fake<ICallerInfoManager>();
        const string SearchTerm = "google"; // Use the passed in parameter in the CallTo setup

        //Define search parameter
        AccountRequest mockAccountRequest = new AccountRequest
        {
            SearchTerm = SearchTerm
        };

        List<Account> expected = new List<Account> { new Account() }; // What we expect to get back

        A.CallTo(() => mockAccountsManager.GetAllWithNameContaining(SearchTerm, A<string>.Ignored)).Returns(expected); // mock the call made in the controller

        using (var accountsController = new AccountController2(mockAccountsManager, mockCallerInfoManager))
        {
            //Act
            List<Account> returnedAccounts = accountsController.Search(mockAccountRequest);

            //Assert
            Assert.AreSame(expected, returnedAccounts);
        }
    }

Do I need to Mock HttpContext?

To get that test working, no. The interface ICallerInfoManager wraps the call to HttpContext and isolates the controller from it, so it will safely run through without hitting HttpContext

That said, if you need to test everything then yes. Your trouble part of the code to test will be:

public CallerInfo GetFrom(HttpRequest request)
{
    return ExtractCallerInfo(request.QueryString);
}

public CallerInfo GetFromHttpContext()
{
    return GetFrom(HttpContext.Current.Request);
}

Because of the hard dependency to HttpContext.

HttpContext and HttpRequest are not that mockable, but have close relations which is. And as @Steve G alluded to in the comments that's quite a big topic.

Upvotes: 1

Related Questions