Mvc 5 unit tests - set viewbag value of controller - c#

I have an index page which could have a ViewBag value of last search. I want to setup my controller so I'm able to set this ViewBag value before calling my system under test (ProductManagementController)
Index action
[HttpPost]
public async Task<ActionResult> Index(ProductManagementVm postedVm)
{
// Reset pagination if new search
if (postedVm.BookSearch != ViewBag.lastSearch)
{
postedVm.Page = 1;
}
var httpResponseMessage = await_httpService.GetAsync(_urlConfigurations.GetProductList);
var vm = _productFactory.BuildProductManagementVm(
await Task.Run(() => httpResponseMessage.Content.ReadAsStringAsync()), postedVm);
vm.BookSearch = postedVm.BookSearch;
if (string.IsNullOrEmpty(postedVm.BookSearch))
{
postedVm.BookSearch = string.Empty;
}
ViewBag.lastSearch = postedVm.BookSearch;
return View(vm);
}
Setup class
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
using System.Web.Mvc;
using ICEBookshop.MVC.App.Controllers;
using ICEBookshop.MVC.App.Interfaces;
using ICEBookshop.MVC.App.Interfaces.Factories;
using ICEBookshop.MVC.App.Models;
using ICEBookshop.MVC.App.Models.ViewModels;
using Moq;
using SpecsFor;
namespace ICEBookshop.MVC.App.Tests.Controllers.ProductManagement
{
public class BaseGiven : SpecsFor<ProductManagementController>
{
protected Mock<IHttpService> HttpServiceMock = new Mock<IHttpService>();
protected Mock<IProductFactory> ProductFactoryMock = new Mock<IProductFactory>();
protected Mock<IUrlConfigurations> UrlConfigurationsMock = new Mock<IUrlConfigurations>();
protected Mock<IJsonValidator> JsonValidatorMock = new Mock<IJsonValidator>();
protected ProductManagementController ProductManagementController;
protected HttpResponseMessage HttpResponseMessage;
protected string JsonContent;
protected bool IsModelStateValid;
protected ActionResult ActionResult;
protected RedirectToRouteResult RedirectToRouteResult;
protected ViewResult ViewResult;
protected ProductManagementVm ProductManagementVm;
protected ProductViewModel ProductViewModel;
protected void BaseGivenSetup()
{
ProductManagementController = new ProductManagementController(HttpServiceMock.Object,
ProductFactoryMock.Object, UrlConfigurationsMock.Object, JsonValidatorMock.Object);
SUT = ProductManagementController;
}
}
}
I would like to set ProductManagementController.ViewBag.SomeName = "some string" so when I step into the controller I test that scenario but at the moment it's null.
Does anyone know how to set a ViewBag value of a controller before testing it?
Unit test
public class WhenServiceReturnProductsAndViewBagHasSearchString : GivenGoingToIndexActionInProductsManagement
{
protected override void When()
{
HttpResponseMessage = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("some string content from the service")
};
HttpServiceMock.Setup(expr => expr.GetAsync(It.IsAny<string>())).ReturnsAsync(HttpResponseMessage);
ProductFactoryMock.Setup(
expr => expr.BuildProductManagementVm(It.IsAny<string>(), It.IsAny<ProductManagementVm>()))
.Returns(new ProductManagementVm());
// This doesn't work :(
SUT.ViewBag.LastSearch = "Hey I'm a search string :D";
BaseGivenSetup();
ActionResult = SUT.Index(new ProductManagementVm()).Result;
ViewResult = (ViewResult)ActionResult;
ProductManagementVm = (ProductManagementVm)ViewResult.Model;
}
[Test]
public void ThenActionResultIsNotNull()
{
Assert.IsNotNull(ActionResult);
}
[Test]
public void ThenAViewResultIsNotNull()
{
Assert.IsNotNull(ViewResult);
}
[Test]
public void ThenProductManagementVmIsNotNull()
{
Assert.IsNotNull(ProductManagementVm);
}
}

ViewBag gets its data from the ViewData property
public dynamic ViewBag
{
get
{
if (_dynamicViewDataDictionary == null)
{
_dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData);
}
return _dynamicViewDataDictionary;
}
}
So you need to populate the value you want there to have access to it in the ViewBag
Here is a POC
[TestClass]
public class ViewBagTests {
[TestMethod]
public void ViewBag_ShouldBe_PrePopulated() {
//Arrange
var SUT = new TargetController();
var expected = "Hey I'm the old search string :D";
SUT.ViewData["LastSearch"] = expected;
//Act
var actual = SUT.Index() as ViewResult;
//Assert
Assert.AreEqual(expected, actual.Model);
}
class TargetController : Controller {
public ActionResult Index() {
var previous = ViewBag.LastSearch;
return View((object)previous);
}
}
}

Related

How to Moq an update method in controller

I am trying to mock a method that updates an entry into the database using EF.
class MyController
{
private readonly IEmployeeRepository;
private readonly IProjectRepository;
MyController(IEmployeeRepository employeeRepository,IProjectRepository projectRepository)
{
_employeeRepository=employeeRepository;
_projectRepository=projectRepository;
}
public async Task<IHttpActionResult> ExtendEmployeeContract(int employeeId, int projectId)
{
var projectModel=_projectRepository.GetProjectDetails(projectId);
int result=-1;
if(projectMode !=null)
{
bool isprojectactive=projectMode.IsActive;
// Some made-up function that is setting the IsContractActive value to true/false
result=_employeeRepository.ExtendContract
(new EmployeeModel(){Id = employeeId,IsContractActive = isprojectactive};);
}
return Ok(new ResponseModel(){Data=result});
}
}
[TestClass]
class MyControllerTestClass
{
private Mock<IEmployeeRepository> _mockedEmployeeRepository;
private Mock<IProjectRepository> _mockedProjectRepository;
ProjectModel _projectModel;
EmployeeModel _empModel;
[TestInitialize]
public void TestInitialize()
{
_mockedEmployeeRepository = new Mock<IEmployeeRepository>();
_mockedProjectRepository = new Mock<IProjectRepository>();
_projectModel = new ProjectModel()
{
Id = 5678,
Name = "testProject",
IsActive=true;
};
_empModel = new EmployeeModel()
{
Id = 123,
IsContractActive = false
};
}
[TestMethod]
public async Task ExtendEmployeeContract_ShouldUpdateAndExtend_EmployeeContract()
{
// Arrange
_mockedProjectRepository.Setup(x=>x.GetProjectDetails(5678)).Returns(_projectModel);
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(_empModel)).Returns(1);
// Act
var mycontroller=new MyController(_mockedEmployeeRepository.object,_mockedProjectRepository.object);
var actionResult = await mycontroller.ExtendEmployeeContract(123,5678) as OkNegotiatedContentResult<ResponseModel>;
// Assert
Assert.AreEqual(1,Convert.ToInt(actionResult.Content.Data.ToString());
}
}
I am trying to mock this controller in my test class . I can successfully mock GetProjectDetails method but I cant mock ExtendContract. I debugged the ExtendEmployeeContract method and found that _employeeRepository.ExtendContract always returns 0
Your fake setup is wrong. Even though you are passing your EmployeeModel
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(_empModel)).Returns(1);
In your controller code you are creating new Employee model, which is different from what you pass in test
result=_employeeRepository.ExtendContract
(new EmployeeModel(){Id = employeeId,IsContractActive = isprojectactive};);
Your fake setup should look like:
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(It.IsAny<EmployeeModel>())).Returns(1);

How to Unit Test a GlassController Action which Returns a View Taking a Model

I'm a sitecore developer and I want to create a sample sitecore helix unit testing project for testing out our "HomeBottomContentController" controller:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
}
I have created a WTW.Feature.HomeBottomContent.Tests project, for the purpose of testing this entire component using helix unit testing. In it I have a UnitTest1.cs file with following:
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
}
This test does pass, meaning "result" is NOT null; however, the problem is when I step into the Index() code, I see that the "model" variable is NULL when we do
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
My question is, how exactly do I change this code to make sure that the "model" in that line does not become null? How do I "mock" an item in unit test code for the _iSitecoreContext so that it has a "Home_Control" template with legit values for its fields? Would that even be the right approach? Most online sources I've found do not have a similar scenario, I'm looking for the shortest code possible.
Another question I had is, how can I test the below Index() method in my [TestMethod], given that the SitecoreContext is declared inside the Index() method, rather than received in the HomeBottomContentController constructor like above? Is there a way to do that from the [TestMethod], or we have to send in the SitecoreContext into the HomeBottomContentController constructor or into the Index() method as a parameter?
public override ActionResult Index()
{
var context = new SitecoreContext();
var model = context.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
In that case you would need to mock the desired behavior on the mocked dependency
[TestClass]
public class UnitTest1 {
[TestMethod]
public void Test_ISitecoreContextInsertion() {
//Arrange
var model = new Home_Control() {
//...populate as needed
}
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
//Setup the method to return a model when called.
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>()).Returns(model);
var controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);
//Act
var result = controllerUnderTest.Index() as ViewResult;
//Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model);
//...other assertions.
}
}
UPDATE
Creating the context within the action tightly couples it to the context, making it almost impossible to mock. That is the reason explicit dependencies are injected
You can do something like that:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = this.GetCurrentItem();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
protected virtual Home_Control GetCurrentItem()
{
return _iSitecoreContext.GetCurrentItem<Home_Control>();
}
}
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
var controllerUnderTest = new FakeHomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
public class FakeHomeBottomContentController : HomeBottomContentController
{
public FakeHomeBottomContentController(ISitecoreContext iSitecoreContext) : base(iSitecoreContext)
{
}
protected override Home_Control GetCurrentItem()
{
// return instance of Home_Control type
// e.g.
return new Home_Control();
}
}
}

Unit Test MVC 5 Controller Create Action with Unit of Work and Repository

I am trying to test a controller action that accepts a view model and creates a new entry. Here is the controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ContactViewModel viewModel)
{
if (!ModelState.IsValid)
return View("Create", viewModel);
_unitOfWork.Contacts.Add(Mapper.Map(viewModel, new Contact()));
_unitOfWork.Complete();
return RedirectToAction("Index");
}
And the unit test:
[TestClass]
public class ContactControllerTests
{
private ContactsController _controller;
private Mock<IContactRepository> _mockRepository;
private string _userId;
private Mock<IUnitOfWork> _mockUoW;
[ClassInitialize]
public static void Init(TestContext context)
{
Mapper.Initialize(c => c.AddProfile<MappingProfile>());
}
[TestInitialize]
public void TestInitialize()
{
_userId = "1";
_mockRepository = new Mock<IContactRepository>();
_mockUoW = new Mock<IUnitOfWork>();
_mockUoW.SetupGet(u => u.Contacts).Returns(_mockRepository.Object);
_controller = new ContactsController(_mockUoW.Object);
_controller.MockCurrentUser(_userId, "user#domain.com");
}
[TestMethod]
public void CreatePost_ValidValuesSubmitted_ShouldCallComplete()
{
var viewModel = new ContactViewModel()
{
FirstName = "a",
LastName = "b"
};
_controller.Create(viewModel);
_mockRepository.Object.GetContacts(_userId).Should().HaveCount(1);
}
}
The unit test always returns the count 0 while I expect it to be 1. I am pretty new to TDD and I implemented unit of work and repository pattern as shown in Mosh Hamedani's course at:
https://app.pluralsight.com/library/courses/full-stack-dot-net-developer-architecture-testing/table-of-contents
You have not mocked any behavior for the repository in the above example.
Based on provided example, let's assume a simple interface like this.
public interface IContactRepository {
void Add(Contact contact);
IEnumerable<Contact> GetContacts(string _userId);
}
You need to have some form of storage for your data.
[TestInitialize]
public void TestInitialize() {
_userId = "1";
var data = new List<Contact>();//To store test data.
//Configure repository
_mockRepository = new Mock<IContactRepository>();
_mockRepository.Setup(m => m.Add(It.IsAny<Contact>())).Callback<Contact>(data.Add);
_mockRepository.Setup(m => m.GetContacts(_userId)).Returns(data);
//Configure UoW
_mockUoW = new Mock<IUnitOfWork>();
_mockUoW.SetupGet(u => u.Contacts).Returns(_mockRepository.Object);
_controller = new ContactsController(_mockUoW.Object);
_controller.MockCurrentUser(_userId, "user#domain.com");
}
or forego the mock and create a fake.
public class FakeContactRepository : IContactRepository {
private ICollection<Contact> data;
public FakeContactRepository(ICollection<Contact> data) {
this.data = data;
}
public void Add(Contact contact) {
data.Add(contact);
}
public IEnumerable<Contact> GetContacts(string _userId) {
return data;
}
}
and set it up for the test.
[TestInitialize]
public void TestInitialize() {
_userId = "1";
var data = new List<Contact>();//To store test data.
//Configure repository
var fakeRepository = new FakeContactRepository(data);
//Configure UoW
_mockUoW = new Mock<IUnitOfWork>();
_mockUoW.SetupGet(u => u.Contacts).Returns(fakeRepository );
_controller = new ContactsController(_mockUoW.Object);
_controller.MockCurrentUser(_userId, "user#domain.com");
}

Is it possible to intercept an action from becoming a ContentResult?

I am attempting to write a filter that wraps data to follow the JSON API spec and so far I've got it working on all cases where I directly return an ActionResult, such as ComplexTypeJSON. I am trying to get it to work in situations like ComplexType where I do not have to run the Json function constantly.
[JSONAPIFilter]
public IEnumerable<string> ComplexType()
{
return new List<string>() { "hello", "world" };
}
[JSONAPIFilter]
public JsonResult ComplexTypeJSON()
{
return Json(new List<string>() { "hello", "world" });
}
However, by the time public override void OnActionExecuted(ActionExecutedContext filterContext) runs when I navigate to ComplexType, the filterContext.Result is a Content Result, that is just a string where filterContext.Result.Content is simply:
"System.Collections.Generic.List`1[System.String]"
Is there a way I can set something up to make ComplexType become JsonResult rather than ContentResult?
For context, here are the exact files:
TestController.cs
namespace MyProject.Controllers
{
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using MyProject.Filters;
public class TestController : Controller
{
[JSONAPIFilter]
public IEnumerable<string> ComplexType()
{
return new List<string>() { "hello", "world" };
}
[JSONAPIFilter]
public JsonResult ComplexTypeJSON()
{
return Json(new List<string>() { "hello", "world" });
}
// GET: Test
[JSONAPIFilter]
public ActionResult Index()
{
return Json(new { foo = "bar", bizz = "buzz" });
}
[JSONAPIFilter]
public string SimpleType()
{
return "foo";
}
[JSONAPIFilter]
public ActionResult Throw()
{
throw new InvalidOperationException("Some issue");
}
}
}
JSONApiFilter.cs
namespace MyProject.Filters
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MyProject.Exceptions;
using MyProject.Models.JSONAPI;
public class JSONAPIFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
private static readonly ISet<Type> IgnoredTypes = new HashSet<Type>()
{
typeof(FileResult),
typeof(JavaScriptResult),
typeof(HttpStatusCodeResult),
typeof(EmptyResult),
typeof(RedirectResult),
typeof(ViewResultBase),
typeof(RedirectToRouteResult)
};
private static readonly Type JsonErrorType = typeof(ErrorModel);
private static readonly Type JsonModelType = typeof(ResultModel);
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (IgnoredTypes.Any(x => x.IsInstanceOfType(filterContext.Result)))
{
base.OnActionExecuted(filterContext);
return;
}
var resultModel = ComposeResultModel(filterContext.Result);
var newJsonResult = new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = resultModel
};
filterContext.Result = newJsonResult;
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
if (modelState == null || modelState.IsValid)
{
base.OnActionExecuting(filterContext);
}
else
{
throw new ModelStateException("Errors in ModelState");
}
}
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception == null) return;
// Todo: if modelstate error, do not provide that message
// set status code to 404
var errors = new List<string>();
if (!(filterContext.Exception is ModelStateException))
{
errors.Add(filterContext.Exception.Message);
}
var modelState = filterContext.Controller.ViewData.ModelState;
var modelStateErrors = modelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList();
if (modelStateErrors.Any()) errors.AddRange(modelStateErrors);
var errorCode = (int)System.Net.HttpStatusCode.InternalServerError;
var errorModel = new ErrorModel()
{
status = errorCode.ToString(),
detail = filterContext.Exception.StackTrace,
errors = errors,
id = Guid.NewGuid(),
title = filterContext.Exception.GetType().ToString()
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = errorCode;
var newResult = new JsonResult() { Data = errorModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
filterContext.Result = newResult;
}
private ResultModel ComposeResultModel(ActionResult actionResult)
{
var newModelData = new ResultModel() { };
var asContentResult = actionResult as ContentResult;
if (asContentResult != null)
{
newModelData.data = asContentResult.Content;
return newModelData;
}
var asJsonResult = actionResult as JsonResult;
if (asJsonResult == null) return newModelData;
var dataType = asJsonResult.Data.GetType();
if (dataType != JsonModelType)
{
newModelData.data = asJsonResult.Data;
}
else
{
newModelData = asJsonResult.Data as ResultModel;
}
return newModelData;
}
}
}
There are two options:
1.use ApiController instead of Controller
The apicontroller will return json result,and the default serializer is Newtonsoft.json(here),so you can use like this below:
//the response type
public class SimpleRes
{
[JsonProperty(PropertyName = "result")]
public string Result;
}
//the controller
public class TestController : ApiController
{
[HttpGet]
[HttpPost]
[JSONAPIFilter]
public SimpleRes TestAction()
{
return new SimpleRes(){Result = "hello world!"};
}
}
2.wrap your response with your own ActionResult if you insist using Controller:
//json container
public class AjaxMessageContainer<T>
{
[JsonProperty(PropertyName = "result")]
public T Result { set; get; }
}
//your own actionresult
public class AjaxResult<T> : ActionResult
{
private readonly T _result;
public AjaxResult(T result)
{
_result = result;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = "application/json";
var result = JsonConvert.SerializeObject(new AjaxMessageContainer<T>
{
Result = _result,
});
var bytes =
new UTF8Encoding().GetBytes(result);
context.HttpContext.Response.OutputStream.Write(bytes, 0, bytes.Length);
}
}
//your controller
[JSONAPIFilter]
public AjaxResult<List<String>> TestSimple()
{
return AjaxResult<List<String>>(new List<string>() { "hello", "world" });
}
and if you wanna get response string from filter for log or something:
var result = filterContext.Response.Content.ReadAsStringAsync();
I think this is what you are looking for :
public class JSONAPIFilterAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
context.Result = new JsonResult
{
Data = ((ViewResult)context.Result).ViewData.Model
};
}
}
From #roosteronacid : return jsonresult in actionfilter
I have just encountered the same issue and found a slightly different approach.
The basic idea was from NOtherDev.
I would introduce an IActionInvoker.
public class ControllerActionInvokerWithDefaultJsonResult : ControllerActionInvoker
{
public const string JsonContentType = "application/json";
protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
{
if (controllerContext.HttpContext.Request.Path.StartsWith("/api/"))
{
return (actionReturnValue as ActionResult)
?? new JsonResult
{
Data = actionReturnValue,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
return base.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue);
}
}
In this case every request starting with "/api/" will have transformed result to json, however only when the actionReturnValue is not a type inherited from ActionResult already.
IActionInvoker is resolved by DependencyResolver, so you need to define the registration in your favorite ioc container which you have set as DependencyResolver.
myFavoriteContainer.Register<IActionInvoker, ControllerActionInvokerWithDefaultJsonResult>(Lifestyle.Transient);
For JsonResult you could use the built-in or this.
In a case you are using async action methods you should inherit from AsyncControllerActionInvoker instead of ControllerActionInvoker and I assume you will need to add an another registration for IAsyncActionInvoker as well. I am not sure about changes in the async part of the invoker itself.

Mocking method used in controller action

public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
return View(model);
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as MyModel;
Assert.IsNotNull(model);
}
this test passes succ. but when when change controller action to have populate combos like
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(id);
return View(model);
}
I'm getting error on line this.PopulatePageCombos(id);
Object reference is not set
So, how can I mock this PopulatePageCombos method in unit test?
Update:
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(model.Id, 100);
return View(model);
}
Update 2:
PopulatePageCombos (model, countryId, requesterId);
where model is of type MyModel, countryId is int and requesterId is int
You can create a helper class PopulatePageCombosHelper and encapsulate PopulatePageCombos method in it. So the SomeAction method would look like
public PopulatePageCombosHelper populatePageHelper;
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
populatePageHelper.PopulatePageCombos(id);
return View(model);
}
So then you can mock populatePageHelper
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
var populatePageHelperMock = new Mock<PopulatePageCombosHelper>();
controller.populatePageHelper = populatePageHelperMock;
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as JobCreate;
//...
Assert.IsNotNull(model);
}
You can make PopulatePageCombos method virtual and override it in derived class ControllerTestable and test the ControllerTestable
public class ControllerTestable : Controller
{
public bool IsCalled = false;
public override ViewResult SomeAction()
{
IsCalled = true;
return null;
}
}
So in the test instead of creating Controller controller you can create ControllerTestable controller.
[Setup]
public void SetUp ()
{
var controller = new ControllerTestable();
//...
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as JobCreate;
//...
Assert.IsTrue(controller.IsCalled);
Assert.IsNotNull(model);
}
You can partial mock the controller. Having:
public virtual void PopulatePageCombos(int? id)
{
throw new NullReferenceException();
}
public ActionResult SomeAction(int? id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(id);
return View(model);
}
Then you setup the method PopulatePageCombos to do anything:
public class Default1ControllerTests
{
private Mock<Default1Controller> controllerMock;
[SetUp]
public void SetUp()
{
this.controllerMock = new Mock<Default1Controller>() { CallBase = true };
this.controllerMock.Setup(m => m.PopulatePageCombos(It.IsAny<int?>())).Callback(() => { });
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controllerMock.Object.SomeAction(null) as ViewResult;
var model = res.Model as MyModel;
Assert.IsNotNull(model);
}
}
It's important to declare virtual the method to mock and specify CallBase = true on mock creation. This will call the programmed logic on methods not setup.

Categories