C# Get return object of CreatedAtRoute - c#

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

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

Tryvalidatemodel in asp.net core webapi throwing null referenceexception in unit test [duplicate]

I'm trying to write unit tests for ModelState validation for an Asp.Net Core Web API.
I read that, the best way to do so is to use TryValidateModel function. But, every time I run the unit test, it throws NullReference exception.
I found many articles suggesting controller.ModelState.AddModelError("",""), but I'm not interested in this, as I believe that it beats the actual purpose of the real model validation.
[TestMethod]
public void TestMethod1()
{
var controller = new TestController();
controller.Post(new Model());
}
public class TestController : Controller
{
public IActionResult Post(Model model)
{
bool b = TryValidateModel(model)
return Ok();
}
}
TryValidateModel(model) always throws NullReference Exception from TryValidateModel(model, prefix) function.
Appreciate any help.
It's configuration/integration issue.
You can see some additional info in the issue in ASP.NET Core repo and another one on github.
But I can tell you the easiest fix (I used it once)
var objectValidator = new Mock<IObjectModelValidator>();
objectValidator.Setup(o => o.Validate(It.IsAny<ActionContext>(),
It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<Object>()));
controller.ObjectValidator = objectValidator.Object;
As I figured how to fix Null Reference Exception thanks to #Egorikas, I noticed that it still doesn't actually validate the model and always returns a true.
I found that we could just use Validator class in System.ComponentModel.DataAnnotationsnamespace.
[TestMethod]
public void TestMethod1()
{
var model = new Person();
var validationResultList = new List<ValidationResult>();
bool b1 = Validator.TryValidateObject(model, new ValidationContext(model), validationResultList);
}
You can directly validate it from the Test method itself, rather than having to call the controller, if ModelState validation is your intention.
Hope this helps.
Based on Andrew Van Den Brink answer's, but with actually having the validation errors set in the ModelState.
private class ObjectValidator : IObjectModelValidator
{
public void Validate(ActionContext actionContext, ValidationStateDictionary validationState, string prefix, object model)
{
var context = new ValidationContext(model, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(
model, context, results,
validateAllProperties: true
);
if (!isValid)
results.ForEach((r) =>
{
// Add validation errors to the ModelState
actionContext.ModelState.AddModelError("", r.ErrorMessage);
});
}
}
Then, simply set the ObjectValidator in your controller:
controller.ObjectValidator = new ObjectValidator();
public class ObjectValidator : IObjectModelValidator
{
private object parameter;
public ObjectValidator(object parameter)
{
this.parameter = parameter;
}
public void Validate(ActionContext actionContext, ValidationStateDictionary validationState, string prefix, object model)
{
var validationContext = new ValidationContext(parameter, null, null);
System.ComponentModel.DataAnnotations.Validator.ValidateObject(model, validationContext);
}
}
var model=new {}; // Model to test
Controller controller = new Controller(); //Controller to test
controller.ObjectValidator = new ObjectValidator(model);
This one will throw exception

How to get the Values from a Task<IActionResult> returned through an API for Unit Testing

I have created an API using ASP.NET MVC Core v2.1. One of my HttpGet methods is set up as follows:
public async Task<IActionResult> GetConfiguration([FromRoute] int? id)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
..... // Some code here
return Ok(configuration);
}
catch (Exception ex)
{
... // Some code here
}
}
When unit testing this I can check that Ok was the response, but I really need to see the values of the configuration. I don't seem to be able to get this to work with the following:
[TestMethod]
public void ConfigurationSearchGetTest()
{
var context = GetContextWithData();
var controller = new ConfigurationSearchController(context);
var items = context.Configurations.Count();
var actionResult = controller.GetConfiguration(12);
Assert.IsTrue(true);
context.Dispose();
}
At runtime, I can check that actionResult has certain values that I am unable to code for. Is there something I am doing wrong? Or am I just thinking about this wrong? I would like to be able to do:
Assert.AreEqual(12, actionResult.Values.ConfigurationId);
You can get tested controller without changing returned type.
IActionResult is base type for all others.
Cast result into expected type and compare returned value with expected.
Since you are testing asynchronous method, make test method asynchronous as well.
[TestMethod]
public async Task ConfigurationSearchGetTest()
{
using (var context = GetContextWithData())
{
var controller = new ConfigurationSearchController(context);
var items = context.Configurations.Count();
var actionResult = await controller.GetConfiguration(12);
var okResult = actionResult as OkObjectResult;
var actualConfiguration = okResult.Value as Configuration;
// Now you can compare with expected values
actualConfuguration.Should().BeEquivalentTo(expected);
}
}
Good practice would suggest that you don't have a lot of code in your controller actions to test and the bulk of logic is in decoupled objects elsewhere that are much easier to test. Having said that, if you still want to test your controllers then you need to make your test async and await the calls.
One of the problems you will have is that you are using IActionResult as it allows you to return BadRequest(...) and Ok(...). However, since you are using ASP.NET MVC Core 2.1, you may want to start using the new ActionResult<T> type instead. This should help with your testing because you can now get direct access to the strongly typed return value. For example:
//Assuming your return type is `Configuration`
public async Task<ActionResult<Configuration>> GetConfiguration([FromRoute] int? id)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
..... // Some code here
// Note we are now returning the object directly, there is an implicit conversion
// done for you
return configuration;
}
catch (Exception ex)
{
... // Some code here
}
}
Note we are now returning the object directly as there is an implicit conversion from Foo to ActionResult<Foo>
Now your test can look like this:
[TestMethod]
public async Task ConfigurationSearchGetTest()
{
var context = GetContextWithData();
var controller = new ConfigurationSearchController(context);
var items = context.Configurations.Count();
// We now await the call
var actionResult = await controller.GetConfiguration(12);
// And the value we want is now a property of the return
var configuration = actionResult.Value;
Assert.IsTrue(true);
context.Dispose();
}
As my reputation does not allow me to comment on #DavidG answer which goes in the right direction, I will put a sample on how to get the value inside Task<IActionResult>.
As # Christopher J. Reynolds pointed out, actionResult.Value can be seen at runtime but not at compilation.
So, I'll show a basic test in which get the Values:
[TestMethod]
public async Task Get_ReturnsAnArea()
{
// Arrange
string areaId = "SomeArea";
Area expectedArea = new Area() { ObjectId = areaId, AreaNameEn = "TestArea" };
var restClient = new Mock<IRestClient>();
restClient.Setup(client => client.GetAsync<Area>(It.IsAny<string>(), false)).ReturnsAsync(expectedArea);
var controller = new AreasController(restClient.Object);
//// Act
// We now await the call
IActionResult actionResult = await controller.Get(areaId);
// We cast it to the expected response type
OkObjectResult okResult = actionResult as OkObjectResult;
// Assert
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
Assert.AreEqual(expectedArea, okResult.Value);
// We cast Value to the expected type
Area actualArea = okResult.Value as Area;
Assert.IsTrue(expectedArea.AreaNameEn.Equals(actualArea.AreaNameEn));
}
For sure this could be improved but I just wanted to show you a simple way to get it.
I hope it helps.
You need to await the call to GetConfiguration to get the IActionResult object back as follows:
var actionResult = await controller.GetConfiguration(12);
To do this you need to change the signature of your test method to be async as well. So change this:
public void ConfigurationSearchGetTest()
To this:
public async Task ConfigurationSearchGetTest()
If you need fast solution,
use JsonConvert.SerializeObject() and after that JsonConvert.DeserializeObject() then you will get Object with values.
[TestMethod]
public async Task ConfigurationSearchGetTest()
{
using (var context = GetContextWithData())
{
var controller = new ConfigurationSearchController(context);
var items = context.Configurations.Count();
var actionResult = await controller.GetConfiguration(12);
var okResult = actionResult as OkObjectResult;
var actualConfiguration = okResult.Value ;
//
//IMPORTANT ONLY BELOW two lines need.
//
var actualConfigurationJStr=JsonConvert.SerializeObject( okResult.Value );
var hereObjectWithStrongType=JsonConvert.DeserializeObject<Configuration>(actualConfigurationJStr);
// Now you can compare with expected values
actualConfuguration.Should().BeEquivalentTo(expected);
}
}

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

MVC - Moq Unit Test FileContentResult (ActionResult) - NullRefernceException

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

Categories