Unit test controller using UrlHelper with NSubstitute - unit-testing

I'm using MVC5 and NSubstitute. I'm trying to create unit test to verify the model is being properly created for some controller actions.
The problem I have is that the controller is using a model within which I have something along the lines of:
Url = new UrlHelper(Request.RequestContext).Action("Browse")
Any time I try to test this controller method, I get a null object exception. I have tried a lot of different ways to try and mock the context, but it never seems to quite work. Can anyone help?
I've referred to many SO question including;
ASP.NET MVC: Unit testing controllers that use UrlHelper
Which isn't so helpful to me as I'm using NSubstitute.
I was trying to use a MockHttpContext helper method I had found:
public static T MockHttpContext<T>(this T controller) where T : Controller
{
controller.ControllerContext = new ControllerContext
{
HttpContext = GetHttpContextMock()
};
controller.Url = new UrlHelper(controller.ControllerContext.RequestContext);
return controller;
}
private static HttpContextBase GetHttpContextMock()
{
var routes = new RouteCollection();
var actionExecutingContext = Substitute.For<ActionExecutingContext>();
var httpContextBase = Substitute.For<HttpContextBase>();
var httpServerUtilityBase = Substitute.For<HttpServerUtilityBase>();
var httpResponseBase = Substitute.For<HttpResponseBase>();
var httpRequestBase = Substitute.For<HttpRequestBase>();
var httpSessionStateBase = Substitute.For<HttpSessionStateBase>();
actionExecutingContext.HttpContext.Returns(httpContextBase);
httpContextBase.Request.ApplicationPath.Returns("");
httpContextBase.Response.ApplyAppPathModifier(Arg.Any<string>())
.Returns(ctx => ctx.Arg<string>());
httpContextBase.Request.Returns(httpRequestBase);
httpContextBase.Response.Returns(httpResponseBase);
httpContextBase.Server.Returns(httpServerUtilityBase);
httpContextBase.Session.Returns(httpSessionStateBase);
httpRequestBase.Cookies.Returns(new HttpCookieCollection());
return httpContextBase;
}

I was trying to do the same thing. This worked for me:
controller.Url = Substitute.For<UrlHelper>();
controller.Url.Action(Arg.Any<string>(), Arg.Any<string>()).Returns("something");
Then when I call my controller action, it does not crash when dereferencing the UrlHelper. Of course you have to mock up (NSubstitute) the controller properly. I hope this works. I can post more code for mocking the controller if you need.

Related

Mock Unit tests for Linq statements in .Net core 2.2 always return null

I know lots of mocking questions are asked, but none worked with me.
I'm trying to write a unit test for a service I have. The service have the following line of code
var assignments = await _assignmentRepository.WhereAsync(as => as.DepartmentId == departmentId);
Here's the implementation of WhereAsync method:
public async Task<List<T>> WhereAsync(Expression<Func<T, bool>> expression)
{
return await _dbContext.Set<T>().Where(expression).ToListAsync();
}
Here's my mock test statement (listAssignments is a predefined variable):
_assignmentRepository.Setup(rep => rep.WhereAsync(as => It.IsAny<bool>())).ReturnsAsync(listAssignments);
I know that we can't Mock Where and FirstOrDefault methods, but isn't there a way to mock my web service WhereAsync method??
As Tseng mentioned in the comment above. We don't mock the DbContext, we mock the repository itself.
So I used the InMemoryDatabase testing. Added some data to my in-memory database which made my DbContext return the data I want.
var mapOptions = new DbContextOptionsBuilder<MapViewerContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var identityOptions = new DbContextOptionsBuilder<AppIdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
var mapContext = new MapViewerContext(_configuration.Object, mapOptions);
var appIdentityContext = new AppIdentityDbContext(_configuration.Object, identityOptions);

Unit testing HttpStatusCode in MVC4 'System.NullReferenceException'

I am trying to unit test HttpStatusCodes in MVC4 but I keep getting a 'System.NullReferenceException' when the controller tries to set the status code on Response, which makes sense as the action is getting called directly. I cant for the life of me work out how to do it without it becoming an integration test. Somebody must have done this, any ideas? See my existing code below.
Controller
public ActionResult Index()
{
Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
Response.Headers.Add("Retry-After", "120");
return View();
}
Test
[Test]
public void IndexActionShouldReturn503StatusCode()
{
//Given
var controller = new HomeController();
//When
var result = controller.Index() as HttpStatusCodeResult;
//Then
result.StatusCode.Should().Be((int)HttpStatusCode.ServiceUnavailable);
}
Note
The requirement is for a friendly 'site down' page so I need to return both a view and the status code.
You're returning a ViewResult, then trying to cast it as a HttpStatusCodeResult in your unit test. Try returning a HttpStatusCodeResult instead of a view.
public ActionResult Index()
{
Response.Headers.Add("Retry-After", "120");
return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}

MVC 3 - Unit Test Controller Result

I am writing unit tests to test MVC 3 controllers. I want to ensure that that the view that comes back from the controller is the right view. In my unit test I have:
[Test]
public void It_Should_Return_The_Right_Page()
{
FormController fc = this.CreateFormController();
var view = fc.FindX();
Assert.AreEqual("FindX", view.ViewName);
}
In my controller, I have:
public ViewResult FindX()
{
return View();
}
This fails because ViewName is null. If I change the call to say return View("FindX") and explicitly define the view to be returned, it works. However, I would like to avoid this if possible. Is there a generally accepted way to approach this?
It sounds like what you want to convey is: Assert that the default view for this method was returned. One way to convey this is using this line:
var view = fc.FindX();
Assert.IsNull(view.ViewName)
But this doesn't convey your intent very well. One way to convey it more clearly is to create an extension method on ActionResult or ViewResult called AssertIsDefaultView like so:
public static class ActionResultAssertions
{
public static void AssertIsDefaultView(this ActionResult actionResult)
{
var viewResult = actionResult as ViewResult;
Assert.IsNotNull(viewResult);
Assert.IsNull(viewResult.ViewName);
}
}
Then in your test you can say:
var view = fc.FindX();
view.AssertIsDefaultView();
MvcContrib has a set of these assertions (I think the name of the method is AssertViewRendered), but I prefer to just write the extensions myself so I can understand MVC better.
If you don't set a viewname, then isn't ViewName being null the correct and expected outcome, so code your test accordingly.
Assert.IsNull(view.ViewName);
that worked for me
public ViewResult FindX()
{
return View("FindX");
}

Is there a way to unit test ASP.NET MVC ViewBag properties set in the view?

Say I have a view with the following code at the top of the page:
#{
ViewBag.Title = "About Us";
Layout = "~/Views/Shared/_Layout.cshtml";
}
And I have a controller method:
public ActionResult About()
{
return View();
}
How can I test that the ViewBag was set properly?
I have tried the following code.
[TestCase]
public void About()
{
var controller = new AboutController();
var ar = controller.About() as ViewResult;
Assert.AreEqual("About Us", ar.ViewBag.Title);
}
But I get the following result when I run the test:
Tests.Controllers.AboutControllerTests.About():
Expected: "About Us"
But was: null
Since both the ViewData and ViewBag use the same storage pattern, you should be able to use ViewData[yourKey] in your tests.
So your test will look like this:
[TestCase]
public void About()
{
var controller = new AboutController();
var ar = controller.About() as ViewResult;
Assert.AreEqual("About Us", ar.ViewData["Title"]);
}
Have you tried
Assert.AreEqual("About Us", controller.ViewBag.Title);
It works for me
The ViewResult returned by a controller has only a reference to the view that should be shown. The view is not even resolved at this time. The code there is never executed by your test.
What you should do is set ViewBag properties in the controller, not the view. Usually, the view will only read such values.
hope it helps
No, you cannot test views like this. The closest you might get is to render the view into a stream writer and then test the generated HTML. It is not something that is commonly done in unit tests. I would recommend you performing web tests in order to verify that the views are correct. You could create web tests with Visual Studio or there's also the free Selenium framework.
For what its worth, I found that the following worked fine:
Assert.AreEqual(_layout, result.ViewBag.Layout);
With result being the ViewResult
[TestMethod]
public async Task GetData() {
CtrlNameController controller = new CtrlNameController();
controller.ViewData["UserId"] = 1;
ViewResult result = await controller.GetData() as ViewResult;
Assert.IsNotNull(result);
}
ViewData["UserId"] is equal to View.UserId

How do I test for TempData being set in MVC 2 where there is a redirect?

When trying to test my MVC 2 controllers, I am having a hard time testing the result of TempData when I'm doing a redirect. It works ok if the result of the controller action is a ViewResult, however, in a redirect, it is RedirectToRouteResult.
So my test is something like this:
var controller = new SubscriptionController(this.dataStorageMock.Object)
{
ControllerContext = MvcMockHelpers.GetControllerContextMock("POST")
};
var actionResult = controller.Create(formCollection);
var redirectResult = (RedirectToRouteResult)actionResult;
// TODO: Need to ensure TempData contains a key "info".
One option is to do the following:
Assert.That(controller.TempData.ContainsKey("info"));
If the result had been a ViewResult it could have been tested like this:
var viewResult = (ViewResult)actionResult;
Assert.That(viewResult.TempData.ContainsKey("info"));
Is there a way to test RedirectToRouteResult the same way as the ViewResult can be tested?
Thanks
Assert.That(controller.TempData.ContainsKey("info")); is exactly what you need.