1

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

        }
    }
NikolaiDante
  • 18,469
  • 14
  • 77
  • 117
jward01
  • 750
  • 4
  • 11
  • 26
  • 2
    A cool way to go about this is to pull out most all logic from the controllers into services. You can unit test those easily. Then you can use integration tests on the controllers. Doesn't really answer your question, but that's what I found works well. – Steve's a D Jan 22 '16 at 17:22
  • Thanks for the answer Steve! That is right I agree, unfortunately I cannot modify any of the code, only write tests. I can modify the code if ABSOLUTELY necessary. – jward01 Jan 22 '16 at 17:29
  • yeah you need to mock httpcontext, if you do a quick google search, there are far more examples than I could post in a comment. – Steve's a D Jan 22 '16 at 17:35
  • http://stackoverflow.com/a/22611611/2881450 -> Don't mock object you don't own. – jHilscher Jan 22 '16 at 19:12
  • While there are all kinds of reasons not to mock `HttpContext` if you can avoid it, if you decide to do so, take a peek at Jonathan Channon's [Mocking HttpContext with Fake It Easy](http://blog.jonathanchannon.com/2013/04/30/mocking-httpcontext-with-fake-it-easy/) – Blair Conrad Jan 23 '16 at 12:18

1 Answers1

1

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.

NikolaiDante
  • 18,469
  • 14
  • 77
  • 117