Using Moq to test methods that accept non-primative arguments - asp.net-mvc

I'm trying to write a test for an ASP.Net MVC controller action.
I'd like to test that the action invokes a particular method on an injected service, so I'm mocking the service and using .Verify.
So in the simple case, I have the following action in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title)
{
_cmsService.AddPage(title);
return View("Edit");
}
using the service interface...
public interface ICmsService
{
void AddPage(string request);
}
and the following test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
cmsService.Setup(service => service.AddPage(pageTitle));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(pageTitle), Times.Once());
Now I want to refactor my service operation to use request and response objects...
public interface ICmsService
{
CmsServiceAddPageResponse AddPage(CmsServiceAddPageRequest request);
}
So I change my action accordingly...
public ActionResult Create(string title)
{
var request = new CmsServiceAddPageRequest()
{
PageName = title
};
var response = _cmsService.AddPage(request);
return View("Edit");
}
and also my test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
var request = new CmsServiceAddPageRequest() {PageName = pageTitle};
cmsService.Setup(service => service.AddPage(request));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(request), Times.Once());
But now when I run the test, I get the following message...
TestCase 'Web.Test.PageControllerTest.CreateNewPagePost'
failed: Moq.MockException :
Invocation was performed more than once on the mock: service => service.AddPage(value(Web.Test.PageControllerTest+<>c__DisplayClass1).request)
at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify[T,TResult](Mock mock, Expression`1 expression, Times times, String failMessage)
at Moq.Mock`1.Verify[TResult](Expression`1 expression, Times times)
PageControllerTest.cs(67,0): at Web.Test.PageControllerTest.CreateNewPagePost()
What should I be doing to test a method that accepts a non-primitive type?
Thanks
Sandy

I think a better alternative to the first answer would be to implement a custom matcher rather than change code to match your testing framework. From:http://code.google.com/p/moq/wiki/QuickStart
// custom matchers
mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge()
{
return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}

If you override Equals in the CmsServiceAddPageRequest object it should compare them correctly.

Related

A little issue with testing an ActionResult that returns a Json

I have an ActionResult like this:
public ActionResult AddDoc(StudentModel studentModel)
{
var student = _studentHelper.GetStudent(studentModel, true);
_updateStudentManager.UpsertStudent(student);
return Json(new { result = true });
}
Test method:
[TestMethod]
public void Calling_AddDoc_Returns_JsonResult()
{
var studentModel = new StudentModel()
{
Name = "Jon",
Id = "1"
};
var studentToAdd = new Student()
{
StudentId = "1",
Name = "Jon",
Course = "SomeCourse"
};
_studentHelper.Setup(x => x.GetStudent(studentModel, false)).Returns(studentToAdd);
var res = _controller.AddDoc(studentModel) as JsonResult;
Assert.AreEqual("{ result = True }", res.Data.ToString());
}
The GetStudent() just maps the incoming object to a new instance of Student and returns it.
The test fails with 'System.NullReferenceException'
Debugging the TestMethod shows that this line
var student = _studentHelper.GetStudent(studentModel, true);
in the controller is not executing at all even when I tried to Step Into the method.
The 'student' object is null.
What am I doing wrong?
Thanks in advance.
Your Setup call has an error. This call expects second parameter to be false.
_studentHelper.Setup(x => x.GetStudent(studentModel, false)).Returns(studentToAdd);
While you are calling from the controller with the value of true.
var student = _studentHelper.GetStudent(studentModel, true);
You are probably using loose behavior of Moq which makes it return null.

Unit Testing a RedirectToAction with parameter

I have an MVC Controller Class that I am trying to Unit Test.
The particular ActionResult is like this
public ActionResult Create(Shipment newShipment)
{
do some stuff to create a shipmentID
...
return RedirectToAction("AddUnit",newShipment.ShipmentID);
}
I have mocked up the controller context etc and now I want to test that the newShipment.ShipmentID passed to the RedirectToAction call is what I expect.
I have a test (with lots of mocking of things in the setup phase)
[Test]
public void CreateSuccess()
{
//Arrange
var shipment = new Shipment();
shipment.Widgets = 2; //Make sure it a valid shipment otherwise
//Act
var result = controller.Create(shipment) as RedirectToRouteResult;
//Assert
Assert.IsNotNull(result);
Assert.AreEqual("AddUnits", result.RouteValues["action"]);
Assert.IsNull(result.RouteValues["controller"]);
...
And now I want to find an Assert to check that the shipmentID I pass to RedirectToAction is the right one. How do I retrieve its value?
(I believe this code works for real (ie the actual view gets the correct shipmentID) but I want to write a unit test ).
Actually, RedirectToActions can take parameters; below is a simplified example of how I tested for correct parameters in one. I'm using NUnit & FluentAssertions here.
[HttpPost]
public ActionResult RedirectMethod()
{
return RedirectToAction("Index", "Home", new { parameter = "Value" });
}
and then the test:
[Test]
public void RedirectSetsExpectedParameters()
{
var result = controller.RedirectMethod();
var redirectResult = result.As<RedirectToRouteResult>();
var expectedRedirectValues = new RouteValueDictionary
{
{ "parameter", "Value" },
{ "action", "Index" },
{ "controller", "Home" }
};
redirectResult
.RouteValues
.ShouldBeEquivalentTo(expectedRedirectValues,
"The redirect should look as I expect, including the parameters");
}
Here i given one sample for Redirect to action
public ActionResult Signupsuccess(string account_code, string plan)
{
..........
do some stuff
return RedirectToAction("SubscriptionPlan", "Settings");
}
I have mocked up controllercontext and passed some parameter to RedirectToAction call from controller to test method.Finally it tested both the expected and actual
[TestMethod()]
public void SignupsuccessTest()
{
HomeController target = new HomeController(); // TODO: Initialize to an appropriate value
string account_code = "B898YB7"; // TODO: Initialize to an appropriate value
string plan = "Application-BASIC"; // TODO: Initialize to an appropriate value
var ControllerContext = new Mock<ControllerContext>();
var context = target.HttpContext;
ControllerContext.SetupGet(p => p.HttpContext.Session["MerchantID"]).Returns("15");
ControllerContext.SetupGet(p => p.HttpContext.Session["FriendlyIdentifier"]).Returns("3d649876-19f5-48d2-af03-ca89083ae712");
target.ControllerContext = controllerContext.Object;
var action = (RedirectToRouteResult)target.Signupsuccess(account_code, plan);
action.RouteValues["action"].Equals("SubscriptionPlan");
action.RouteValues["controller"].Equals("Settings");
Assert.AreEqual("SubscriptionPlan", action.RouteValues["action"]);
Assert.AreEqual("Settings", action.RouteValues["controller"]);
}
I Hope really it will helpful for you.Thank You

Expression Equality or Equivalence from Multiple Projects

It is understood that:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.Equals(second)); // Output is "False"
However, examining the strings of each expression does show equality:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.ToString().Equals(second.ToString())); // Output is "True"
This idea was a culmination of different posts...
http://www.codethinked.com/Comparing-Simple-Lambda-Expressions-With-Moq
Moq'ing methods where Expression<Func<T, bool>> are passed in as parameters
Verify method was called with certain linq expression (moq)
The intent:
I am writing an MVC application using the repository pattern such that
public class MyController : Controller
{
public Repository.IRepository Repository { get; set; }
public MyController()
{
this.Repository = new Repository.CommonRepository();
}
public MyController(Repository.IRepository repository)
{
this.Repository = repository;
}
[HttpPost]
public ActionResult Create(Domain.Common.Object1 o1)
{
if (ModelState.IsValid)
{
// Additional validation
o1.Name = o1.Name.Trim();
if (this.Repository.Any<Domain.Common.Object1>(a => a.Name.ToLower() == plant.Name.ToLower()))
this.ModelState.AddModelError("Name", "Duplicate found.");
}
if (ModelState.IsValid)
{
var entity = this.Repository.Add(o1);
if (Request.IsAjaxRequest())
return this.Json(new { Completed = true, Id = entity.Id });
return RedirectToAction("Details", new { id = entity.Id });
}
if (Request.IsAjaxRequest())
return PartialView("_Create", o1);
return View("Create", o1);
}
}
Repository is a completely separate project as is the domain. My repository code is setup so that I can use the one repository to query any object based upon the generic:
public IQueryable<T> GetAll<T>() where T : AbstractEntity
{
return this.DbContext.Set<T>();
}
Note: AbstractEntity is a domain abstract class all of my POCO objects inherit from.
Everything is fine when using Moq to unit test the controller :
[TestMethod]
public void Create_Post_DuplicateNameAddsError()
{
// Arrange
var repository = new Mock<Repository.IRepository>();
repository.Setup(a => a.Any<Domain.Common.Object1>(It.IsAny<System.Linq.Expressions.Expression<Func<Domain.Common.Object1, bool>>>()))
.Returns(true);
var controller = ControllerFactory<MyController>.GetController();
controller.Repository = repository.Object;
var model = new Domain.Common.Object1()
{
Id = Guid.NewGuid()
,
Name = "Name"
};
// Act
var result = controller.Create(model) as ViewResult;
// Assert
Assert.IsFalse(controller.ModelState.IsValid);
Assert.IsNotNull(result);
Assert.AreEqual("Create", result.ViewName, false);
Assert.AreEqual(model, result.Model);
}
Note: ControllerFactory is a way to generate a controller with certain properties filled, such as Request, Response, User, Request.Headers ect...
Where this fails is if I have to use IRepository.Any(predicate) more than once, or any method that uses expressions that is called more than once. I need it to say true for one and false for another. If the expression strings were a match, this would be a non-issue, but as everything is in different projects the expression strings come out as:
a => (a.Name.ToLower() == value(foo.Web.Tests.Controllers.Object1ControllerTests+<>c__DisplayClass3).ob1.Name.ToLower())
a => (a.Name.ToLower() == value(foo.Controllers.MyController+<>c__DisplayClass1).ob1.Name.ToLower())
The difference lies in the value function. I have tried matching from Regular Expressions, which works, but is ugly as you have to escape every .<>(), which in turn makes it very difficult to maintain.
I tried using Matt Meber's Expression Equality Comparer, but they are not equal due to that value function (my belief).
Suggestions?

using HTTPContext variable in POCO Model class in asp.net MVC

Controller uses HttpContext request object to get relevant information and passes it along to viewmodel.
string user = HttpContext.Request.Headers["abc"]
Question is how to pass the same information i.e. HttpContext to POCO model where i have to set some parameters. i can't user System.Web.MVC
public class Test
{
public string userA;
public Test()
{
userA = "Here I want to get the user from HttpContext and set it";
}
}
creating HTTPContextBase object using HTTPContext.Current throws error
"Request is not available in current context"
any help would be appreciated.thanks
Consider using a unmapped property (or a function that sets a private variable) in your model to store the value of whatever information you need from the httpcontext.
Your model should not know about the httpcontext. Models should be separated from the environment in which they are being used.
Another idea is that if you need to do business logic using a service layer where the service has context information passed to it by the controller:
http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs
While it is a very good idea not to bound your Controllers to the HttpContext (as scott.korin mentions) I've found that there's some times no way around it (like when testing Routes).
Here's the code that we use, that is based on Steve Sanderson's book "Pro ASP.NET MVC 3 Framework". Pay close attention to the CreateHttpContext method.
private void TestRouteMatch(string url, string controller, string action, object routeProperties = null, string httpMethod = "GET")
{
// Arrange
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
// Act - process the route
RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod));
// Assert
Assert.IsNotNull(result);
Assert.IsTrue(TestIncomingRouteResult(result, controller, action, routeProperties));
}
private bool TestIncomingRouteResult(RouteData routeResult, string controller, string action, object propertySet = null)
{
Func<object, object, bool> valCompare = (v1, v2) =>
{
return StringComparer.InvariantCultureIgnoreCase.Compare(v1, v2) == 0;
};
bool result = valCompare(routeResult.Values["controller"], controller)
&& valCompare(routeResult.Values["action"], action);
if (propertySet != null)
{
PropertyInfo[] propInfo = propertySet.GetType().GetProperties();
foreach (PropertyInfo pi in propInfo)
{
if (!(routeResult.Values.ContainsKey(pi.Name)
&& valCompare(routeResult.Values[pi.Name],
pi.GetValue(propertySet, null))))
{
result = false;
break;
}
}
}
return result;
}
private void TestRouteFail(string url)
{
// Arrange
RouteCollection routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
// Act - process the route
RouteData result = routes.GetRouteData(CreateHttpContext(url));
// Assert
Assert.IsTrue(result == null || result.Route == null);
}
private HttpContextBase CreateHttpContext(string targetUrl = null, string httpMethod = "GET")
{
// create the mock request
Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
mockRequest.Setup(m => m.AppRelativeCurrentExecutionFilePath).Returns(targetUrl);
mockRequest.Setup(m => m.HttpMethod).Returns(httpMethod);
// create the mock response
Mock<HttpResponseBase> mockResponse = new Mock<HttpResponseBase>();
mockResponse.Setup(m => m.ApplyAppPathModifier(
It.IsAny<string>())).Returns<string>(s => s);
// create the mock context, using the request and response
Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>();
mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
mockContext.Setup(m => m.Response).Returns(mockResponse.Object);
// return the mocked context
return mockContext.Object;
}

Mocking Ajax.IsRequest to return False

I am trying to mock the Ajax.IsRequest() method of ASP.Net MVC. I found out how to do it in order for it to return true:
Expect.Call(_myController.Request.Headers["X-Requested-With"]).Return("XMLHttpRequest").Repeat.Any();
This works and returns true. Now I need to test the other branch of the code. How can I mock it to return false? I have tried removing the mock altogether, It fails with:
System.NullReferenceException : Object
reference not set to an instance of an
object.]
If I do:
Expect.Call(_templateReportController.Request["X-Requested-With"]).Return(null).Repeat.Any();
It fails with the same error.
Entire Test:
/// <summary>
/// Tests the Edit Action when calling via Ajax
/// </summary>
[Test]
public void Test_Edit_AjaxRequest()
{
Group group = new Group();
group.ID = 1;
group.Name = "Admin";
IList<Group> groupList = new List<Group>() { group };
Definition def = new Definition();
def.ID = 1;
def.Name = "Report";
def.LastModified = DateTime.UtcNow;
def.Groups.Add(group);
using (_mocks.Record())
{
Expect.Call(_myController.Request["X-Requested-With"]).Return("XMLHttpRequest").Repeat.Any();
Expect.Call(_DefBiz.GetAll<Group>()).Return(groupList);
Expect.Call(_DefBiz.Get<Definition>(1)).Return(def);
}
myController.DefAccess = _DefBiz;
PartialViewResult actual;
using (_mocks.Playback())
{
actual = (PartialViewResult)myController.Edit(1);
}
}
Any advices?
Cheers
The reason your are getting NullReferenceException is because you never stubbed the controller.Request object in your unit test and when you invoke the controller action which uses Request.IsAjaxRequest() it throws.
Here's how you could mock the context using Rhino.Mocks:
[TestMethod]
public void Test_Ajax()
{
// arrange
var sut = new HomeController();
var context = MockRepository.GenerateStub<HttpContextBase>();
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.Stub(x => x.Request).Return(request);
// indicate AJAX request
request.Stub(x => x["X-Requested-With"]).Return("XMLHttpRequest");
sut.ControllerContext = new ControllerContext(context, new RouteData(), sut);
// act
var actual = sut.Index();
// assert
// TODO: ...
}
[TestMethod]
public void Test_Non_Ajax()
{
// arrange
var sut = new HomeController();
var context = MockRepository.GenerateStub<HttpContextBase>();
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.Stub(x => x.Request).Return(request);
sut.ControllerContext = new ControllerContext(context, new RouteData(), sut);
// act
var actual = sut.Index();
// assert
// TODO: ...
}
And here's a better alternative (which I would personally recommend you) in order to avoid all the plumbing code. Using MVCContrib.TestHelper (which is based on Rhino.Mocks) your unit test might be simplified to this:
[TestClass]
public class HomeControllerTests : TestControllerBuilder
{
private HomeController _sut;
[TestInitialize()]
public void MyTestInitialize()
{
_sut = new HomeController();
this.InitializeController(_sut);
}
[TestMethod]
public void HomeController_Index_Ajax()
{
// arrange
_sut.Request.Stub(x => x["X-Requested-With"]).Return("XMLHttpRequest");
// act
var actual = _sut.Index();
// assert
// TODO: ...
}
[TestMethod]
public void HomeController_Index_Non_Ajax()
{
// act
var actual = _sut.Index();
// assert
// TODO: ...
}
}
Much prettier. It also allows you to write much more expressive asserts on the action results. Checkout the doc or ask if for more info is needed.
Expect.Call(_myController.Request.Headers["X-Requested-With"]).Return("SpitAndDuctTape").Repeat.Any();
...should do the job.

Resources