MVC - Moq Unit Test FileContentResult (ActionResult) - NullRefernceException - c#

So I'm posting to an MVC controller, which makes a call to a repository to get a Telerik report, then exports a PDF. I'm having trouble unit testing this and keep getting an error -
System.NullReferenceException: Object reference not set to an instance of an object.
Controller
public class ReportController : Controller
{
private IPDFRepository _pdfRepository;
//Dependency Injection using Unity.MVC5 NuGet Package
public ReportController(IPDFRepository pdfRepository)
{
_pdfRepository = pdfRepository;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PDFExport(PDFViewModel model)
{
byte[] report = _pdfRepository.BuildExport(model);
return File(report, "application/pdf", model.SelectedReport + ".pdf");
}
}
Unit Test
[TestMethod]
public void Report_PDFExport_Returns_ActionResult()
{
//Arrange
var mockRepository = new Mock<IPDFRepository>();
mockRepository.Setup(x => x.BuildExport(It.IsAny<PDFViewModel>()));
ReportController controller = new ReportController(mockRepository.Object);
//Act
ActionResult result = controller.PDFExport(It.IsAny<PDFViewModel>());
//Assert
Assert.IsInstanceOfType(result, typeof(ActionResult));
}
Now, I realize this has something to do with this return portion of my controller.
return File(report, "application/pdf", model.SelectedReport + ".pdf");
I can change that around to return string, test again and get this to work.
Also, if I comment out these last two lines of the unit test,
//Act
//ActionResult result = controller.PDFExport(It.IsAny<PDFViewModel>());
//Assert
//Assert.IsInstanceOfType(result, typeof(ActionResult));
it will run without error. I can't figure out how to get around the null reference.

You are not setting up the mock of IPDFRepository properly. It needs to configure what it is going to return when BuildExport is called. Otherwise report will be null.
And you are also not calling the method under test with a valid parameter. You need to create a concrete instance other wise the model will be null and model.SelectedReport with error out.
[TestMethod]
public void Report_PDFExport_Returns_ActionResult()
{
//Arrange
byte[] fakePDFReport = new byte[0];
var mockRepository = new Mock<IPDFRepository>();
mockRepository
.Setup(x => x.BuildExport(It.IsAny<PDFViewModel>()))
.Returns(fakePDFReport);
var fakeViewModel = new PDFViewModel {
SelectedReport = "FakeReportName"
//Set the needed properties...
};
ReportController controller = new ReportController(mockRepository.Object);
//Act
ActionResult result = controller.PDFExport(fakeViewModel);
//Assert
Assert.IsInstanceOfType(result, typeof(ActionResult));
}

Related

How do you test for NotFound() in ASP.Net Core MVC

Hi I am trying to write Unit Tests for my controller, this is my fist test I have written, well, trying to write.
In my controller I have the method -
public IActionResult Details(int id)
{
var centre = _centreRepository.GetCentreById(id);
if (centre == null)
{
return NotFound();
}
return View(Centre);
}
I am trying to write a test so that it passes when NotFound() is returned.
For my test I have -
[Test]
public void TestVaccinationCentreDetailsView()
{
var centrerepository = new Mock<ICentreRepository>();
var controller = new CentreController(centrerepository.Object);
var result = controller.Details(99);
Assert.AreEqual(404, result.StatusCode);
}
When run result returns Microsoft.AspNetCore.Mvc.NotFoundResult object, which has status code of 404.
result.StatusCode does not exist.
I am confused.
I am using .Net 5, ASP.Net core MVC 5.
Can anyone help please?
Thank you.
It is enough to just test if the result is of NotFoundResult type
var result = controller.Details(99);
//I prefer this one
Assert.IsInstanceOf<NotFoundResult>(result);
//other possible solution
Assert.IsTrue(result is NotFoundResult);
The controller action is returning an abstraction. ie IActionResult
Cast the result in the test to the expected type and assert on that
[Test]
public void TestVaccinationCentreDetailsView() {
//Arrange
var centrerepository = new Mock<ICentreRepository>();
var controller = new CentreController(centrerepository.Object);
//Act
var result = controller.Details(99) as NotFoundResult; //<-- CAST HERE
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(404, result.StatusCode);
}

C# Get return object of CreatedAtRoute

I have a controller method which returns a CreatedAtRoute object. How can I extract the returned object?
My test class (which calls the controller):
[Test]
public void AddUser(){
AppUser au= new AppUser();
GreenCardController gc = new GreenCardController();
ActionResult<AppUser> res = gc.UpdateArgosUser(au); //<-- How can I turn this into an AppUser object?
//var temp=res.Value; //this didnt have the Object
Assert.AreEqual(res.FirstName,"")
}
Controller method:
public ActionResult<AppUser> UpdateArgosUser([FromBody]AppUser au)
{
return CreatedAtRoute(nameof(GreenCardController.GetArgosUser), new
{ userid = au.UserId }, au);
}
When I paused this in debug mode res.Result.Value seemed to be what I was looking for, but I get the error:
ActionResult does not contain a definition for 'Value'
Extract the desired values from within the returned ActionResult<AppUser>, casting to the known types to get access to the desired members.
Since the controller call return CreatedAtRoute(...), then you need to access the ActionResult<TResult>.Result property to get the returned ActionResult from the controller.
From there it is a matter of getting the stored value object within the object result.
[Test]
public void AddUser(){
//Arrange
AppUser expected = new AppUser();
GreenCardController controller = new GreenCardController();
//Act
ActionResult<AppUser> actionResult = controller.UpdateArgosUser(expected);
CreatedAtRouteResult result = actionResult.Result as CreatedAtRouteResult;
AppUser actual = result.Value as AppUser;
//Assert
Assert.AreEqual(expected, actual);
}

ViewBag property always returning null in unit test

I am trying to test that ViewBag data is being populated from my controller, but in my unit tests my property returns null no matter what I set.
controller:
public ActionResult Index()
{
_logger.LogEvent(LogLevel.Trace, null, $"Landing page requested", null);
ViewBag.InstrumentationKey = _instrumentationKey;
return View("Index");
}
unit test:
[TestMethod]
public void TestIndexHasApplicationInsightsKey()
{
// Arrange
var mock = new Mock<ILogging>();
mock.Setup(logging => logging.LogEvent(It.IsAny<LogLevel>(), It.IsAny<Exception>(), It.IsAny<string>(), It.IsAny<object[]>())).Verifiable();
HomeController controller = new HomeController(mock.Object);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.AreNotEqual(null, result.ViewBag.InstrumentationKey as string);
}
result:
Assert.AreNotEqual failed. Expected any value except:<(null)>. Actual:<(null)>.
I read a few answers that said I should be accessing my property view ViewData["InstrumentationKey"], but that always returns null also.
Any idea what I am doing wrong that wouldn't allow me to test the ViewBag properties value?
For demonstrative purposes the following was tested
[TestClass]
public class MyViewBagTestClass {
[TestMethod]
public void TestIndexHasApplicationInsightsKey() {
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.AreNotEqual(null,result.ViewBag.InstrumentationKey as string);
}
}
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.InstrumentationKey = "Hello world";
return View("Index");
}
}
to prove that it does in fact work when tested. Which it does, as it passes when tested.
This would lead me to believe that _instrumentationKey in your method under test is in fact null when the test is exercised.
I suggest you review when that variable is populated, making sure that a value is assigned during the exercising of the method under test.

Write Unit test for [frombody] data binding returns null C#

I would like to write a unit test for [frombody] data binding that returns null in C#.
So I've got this model:
public class Model
{
public int number{ get; set; }
}
And that is the Action for the web service:
[HttpPost]
public IActionResult API([FromBody]Model model)
{
if (model== null)
{
return Json(new { error = "Could not decode request: JSON parsing failed" });
}
//some logic to get responsesToReturn;
return Json(responsesToReturn);
}
So I used the built-in data-binding to check the validity of the passed-in data.Say if the client send a Json number : "abc", the model object will become null after the data-binding. (Because "abc" is not convertible to int)
So I would like to write a Unit test for this behaviour.Here is my current test:
[TestClass]
public class ModelControllerTest
{
[TestMethod]
public void TestAPIModelIsNull()
{
var controller = new ModelController();
Model model = null;
var result = controller.API(model);
object obj = new { error = "Could not decode request: JSON parsing failed" };
var expectedJson = JsonConvert.SerializeObject(obj);
Assert.AreEqual(expectedJson, result);
}
}
I kept getting this System.NullReferenceException: Object reference not set to an instance of an object. error. I am guessing because I explicitly set the model to null, but the action expect a instance of the Model. But in the application, the data-binding does return null when the request data in invalid.
So the question is how do I write a unit test for [frombody] data-binding return null?
I found out the reason. It was not because that I cannot assign an object to null. It was because that when I ran the test, the Response.StatusCode = 400 in the controller give me the System.NullReferenceException Because the Reponse in the test controller is null.
So I just set the Response in my test controller like so:
[TestMethod]
public void TestAPIShowInfoIsNull()
{
//arrange
var controller = new ShowsInfoController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
var response = controller.ControllerContext.HttpContext.Response;
//act
ShowsInfo showsInfo = null;
var result = controller.API(showsInfo);
//assert
Assert.AreEqual(400, response.StatusCode);
Assert.IsInstanceOfType(result, typeof(JsonResult));
}

Unit Testing Controller Actions that call IsAjaxRequest()

Some of my controller actions need to respond with different ViewResults depending whether or not they were called by an AJAX request. Currently, I'm using the IsAjaxRequest() method to check for this. When this method is called during a unit test, it throws an ArgumentNullException because the HTTP context is missing.
Is there a way to mock/fake this call? Or is this a sign I should be checking for an AJAX request another way?
Would it help if you provide a Test Double for the HTTP Context?
This can be done like this:
var httpCtxStub = new Mock<HttpContextBase>();
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = httpCtxStub.Object;
sut.ControllerContext = controllerCtx;
where sut represents the System Under Test (SUT), i.e. the Controller you wish to test.
This example uses Moq.
Using moq library in MVC test projects
[TestClass]
public class HomeControllerTest
{
[TestMethod]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
controller.injectContext();
// controller.injectContext(ajaxRequest: true);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
}
public static class MvcTestExtensions
{
public static void injectContext(this ControllerBase controller, bool ajaxRequest = false)
{
var fakeContext = new Mock<ControllerContext>();
fakeContext.Setup(r => r.HttpContext.Request["X-Requested-With"])
.Returns(ajaxRequest ? "XMLHttpRequest" : "");
controller.ControllerContext = fakeContext.Object;
}
}

Categories