I'm creating a unit test using nunit and all of this code works fine in runtime.
I have this protected HttpResponseMessage code below that is being called by my controller when it returns.
However, an error:
"Value cannot be null. Parameter name: request" is displaying.
And when I check the request, it is actually null.
Question:
How will I code my unit test to return the HttpResponseMessage?
Error is shown in this line:
protected HttpResponseMessage Created<T>(T result) => Request.CreateResponse(HttpStatusCode.Created, Envelope.Ok(result));
Here is my Controller:
[Route("employees")]
[HttpPost]
public HttpResponseMessage CreateEmployee([FromBody] CreateEmployeeModel model)
{
//**Some code here**//
return Created(new EmployeeModel
{
EmployeeId = employee.Id,
CustomerId = employee.CustomerId,
UserId = employee.UserId,
FirstName = employee.User.FirstName,
LastName = employee.User.LastName,
Email = employee.User.Email,
MobileNumber = employee.MobileNumber,
IsPrimaryContact = employee.IsPrimaryContact,
OnlineRoleId = RoleManager.GetOnlineRole(employee.CustomerId, employee.UserId).Id,
HasMultipleCompanies = EmployeeManager.HasMultipleCompanies(employee.UserId)
});
}
The reason why you are getting:
An exception of type 'System.ArgumentNullException' occurred in System.Web.Http.dll but was not handled in user code
Additional information: Value cannot be null.
is because the Request object is null.
The solution for that is to create an instance of your controller in your tests such as:
var myApiController = new MyApiController
{
Request = new System.Net.Http.HttpRequestMessage(),
Configuration = new HttpConfiguration()
};
In this way, when creating a new instance of the MyApiController class we are initializing the Request object. Moreover, it is also necessary to provide the associated configuration object.
Finally, an example of Unit Test for your Api Controller could be:
[TestClass]
public class MyApiControllerTests
{
[TestMethod]
public void CreateEmployee_Returns_HttpStatusCode_Created()
{
// Arrange
var controller = new MyApiController
{
Request = new System.Net.Http.HttpRequestMessage(),
Configuration = new HttpConfiguration()
};
var employee = new CreateEmployeeModel
{
Id = 1
};
// Act
var response = controller.CreateEmployee(employee);
// Assert
Assert.AreEqual(response.StatusCode, HttpStatusCode.Created);
}
}
I think what happens is that you are not instantiating or assigning your Request property (HttpRequestMessage) when you new up your Controller. I believe it's mandatory to specify the request prior calling into the Api method via your unit test.
You may also require a Configuration (HttpConfiguration):
sut = new YourController()
{
Request = new HttpRequestMessage {
RequestUri = new Uri("http://www.unittests.com") },
Configuration = new HttpConfiguration()
};
Let me know if that works.
Also, if your controller has injections, you can do:
var controller= new MyController(injectionA, injectionB, injectionC)
{
Request = new HttpRequestMessage(),
Configuration = new HttpConfiguration()
};
I find them all on the easy to understand official doc now.
Related
I am using Core 3.1 Xunit to test a Razor Page in an application, with Moq to mock any services. When running a test on a handler of PageModel to test if it returns PartialViewResult type I get the following exception:
System.NullReferenceException : Object reference not set to an instance of an object.
The PageModel handler looks like this, with Application as a property on PageModel:
public PartialViewResult OnGetApplicationAddedModalPartial()
{
return Partial("_ApplicationAddedModalPartial", Application);
}
And this is the failing test for it:
[Fact]
public void OnGetApplicationAddedModalPartial_WhenCalled_ShouldReturPartialViewResultType()
{
//Arrange
_sut.Application = It.IsAny<ApplicationModel>();
//Act
var result = _sut.OnGetApplicationAddedModalPartial();
//Assert
Assert.IsType<PartialViewResult>(result);
}
What's curious is that I have another handler in that PageModel that I successfully test to see if the return is of JasonResult type. Additionally, I have controllers in this application of which some actions also return a PartialViewResult type that I successfully test for.
What is special about a handler of return type PartialViewResult on a PageModel and how can I create a test to ensure that it returns PartialViewResult?
When testing a PartialViewResult, you need to instantiate a number of properties of the PageModel as part of your setup: https://github.com/dotnet/AspNetCore.Docs/issues/24312
// Arrange
var httpContext = new DefaultHttpContext();
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
var modelMetadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
var pageContext = new PageContext(actionContext)
{
ViewData = viewData
};
var pageModel = new MyPageModel()
{
PageContext = pageContext,
TempData = tempData,
Url = new UrlHelper(actionContext),
MetadataProvider = modelMetadataProvider
};
// Act
var result = await pageModel.GetPartial();
// Assert
Assert.IsType<PartialViewResult>(result);
I'm using .Net Core 3.1 and I'm having trouble sending requests from a Blazor component. I want to send a request to a controller I have, and these requests systematically end up in 400 Bad request.
In my Startup.cs, I have
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
{
services.AddScoped<HttpClient>(s =>
{
var uriHelper = s.GetRequiredService<NavigationManager>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.BaseUri)
};
});
}
In my Blazor component, I have:
var json2 = Newtonsoft.Json.JsonConvert.SerializeObject(_Model);
var stringContent2 = new StringContent(json2, System.Text.Encoding.UTF8, "application/json");
var response2 = await Http.PostAsync("/[controllerName]/[Method]", stringContent2);
if (response2.IsSuccessStatusCode)
{
var resultContent = response2.Content.ReadAsStringAsync().Result;
return resultContent;
}
else
return "failed";
And here is my Controller Method prototype:
[HttpPost]
public IActionResult Method([FromBody] Model form)
{...}
Would you happen to see what's wrong with the code?
You are passing a StringContent object in your PostAsync method, but in your action you have your Model as a parameter.
You have two options :
To change your action parameter to a StringContent.
To parse the Json as your Model to pass it to the PostAsync method content parameter.
Regards,
these requests systematically end up in 400 Bad request.
Please check you provide correct request header(s) and well-formatted data while you make request from your Blazor app to backend service.
I did a test using following code snippet with simple testing data, which work well on my side. If possible, you can create a new component and test if the code snippet can work for you.
var _Model = new Model { Id = 1, Name = "fehan" };
var json2 = Newtonsoft.Json.JsonConvert.SerializeObject(_Model);
var stringContent2 = new StringContent(json2, System.Text.Encoding.UTF8, "application/json");
var response2 = await Http.PostAsync("Home/Method", stringContent2);
if (response2.IsSuccessStatusCode)
{
var resultContent = response2.Content.ReadAsStringAsync().Result;
}
Model class
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
}
Test Result
Besides, if you are making request to MVC controller action, please check if you enabled antiforgery validation on controller or action(s).
Im trying with this TestMethod
[TestMethod]
[Description("Checks if the SearchResults Controller is generating any data from the Report")]
public async Task GetAllOldCustomersContainingTerm_FromSearchResultsControllerTest()
{
// Create mock configuration files for every class
DalConfig config = new DalConfig()
{
ConnectionString = "Trusted_Connection=True;database=AdventureWorks2017;Server=localhost\\MSSQL2017",
};
// Create mock options, PrintService and logger
var mockOptions = new Mock<IOptions<DalConfig>>();
mockOptions.Setup(op => op.Value).Returns(config);
var searchResultFunctions = new SearchResultFunctions();
var logger = new Logger<SearchResultsController>(new LoggerFactory());
var mockSearchResultServices = new Mock<SearchResultService>().As<ISearchResultService>();
mockSearchResultServices.CallBase = true;
// Terms to test. In MockDatabase, John exist on FirstName and 12345 on PostalCode
var terms = new SearchTerms
{
FirstName = "John",
PostalCode = "123456"
};
mockSearchResultServices.Setup(x => x.GetAllOldCustomersContainingTermAsync(config, terms))
.ReturnsAsync(new WebApiMockDatabaseRecordsProvider().GetAllMockOldCustomersDtos());
// Create mock controller
var testController = new SearchResultsController(logger, mockSearchResultServices.Object, searchResultFunctions, mockOptions.Object);
var result = await testController.GetAllOldCustomersContainingTermAsync() as OkObjectResult;
// Check if data is being returned from the Controller
Assert.IsTrue(result.Value != null);
To test the following controller.
Before i use QueryString i had a HttpGet with parameters and the test was succesful:
[ApiController]
[Route("[controller]")]
public class SearchResultsController : ControllerBase
{
readonly ILogger<SearchResultsController> _logger;
ISearchResultService ResultService;
ISearchResultFunctions ResultFunctions;
DalConfig DalConfig;
public SearchResultsController(ILogger<SearchResultsController> logger
, ISearchResultService resultService, ISearchResultFunctions resultFunctions, IOptions<DalConfig> settings)
{
DalConfig = settings.Value;
ResultService = resultService;
ResultFunctions = resultFunctions;
_logger = logger;
}
/// <summary>
/// Returns all customers with values that matches in any of the terms
/// Searches all the customer fields except BusinessEntityId and BirthDate
/// </summary>
/// <param name="terms">a list of string terms, seperated by space</param>
/// <returns></returns>
[HttpGet("FindOldCustomers/{terms?}")]
public async Task<IActionResult> GetAllOldCustomersContainingTermAsync()
{
//if (terms == null)
var terms = new SearchTerms()
{
FirstName = Request.Query["FirstName"],
LastName = Request.Query["LastName"],
EmailAddress = Request.Query["EmailAddress"],
Gender = Request.Query["Gender"],
AddressLine1 = Request.Query["AddressLine1"],
AddressLine2 = Request.Query["AddressLine2"],
City = Request.Query["City"],
JobTitle = Request.Query["JobTitle"],
PhoneNumber = Request.Query["PhoneNumber"],
PostalCode = Request.Query["PostalCode"],
};
var config = new DalConfig()
{
ConnectionString = DalConfig.ConnectionString,
};
var task = await ResultService.GetAllOldCustomersContainingTermAsync(config, terms);
if (task.Count == 0)
return NotFound();
return Ok(ResultFunctions.ConvertToJSON(task));
}
Im getting null reference exception, im not sure why.
Also im not sure if i somehow must pass to the controler the terms object. With parameters i was passing the terms from parameters.
I don't think the null check (as suggested in your answer) is the right approach here. While I wouldn't normally argue against that, there would be no way that the Request object could be null in a production environment. In fact, by adding the null check, you are allowing the test to branch such that it is not really testing the functionality of the method as used in production.
The real solution would be to mock the Request so that you can provide the query string parameters that are expected by the Controller method. See this answer for some advice on that.
I Added in the controller a check to see if Request is null and now test passes
Here's a sample of one of my unit test classes (pared down to the basics). In the controller, when the Index() action method is invoked, a call to GetByID(1234) always results in a newed up instance of a Ticket object. The object exists, but all of its properties are null, even though I've set them in my fake object. Any ideas as to why?
I'm using Moq.
Unit test
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>();
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
Mock<HttpContextBase> context = FakeHttpContext();
context.Setup(ctx => ctx.Session[SessionKeys.TokenData.ToString()]).Returns(Constants.TOKENDATA_SUBMITTER);
MockTicketRepository.Setup(x => x.GetById(It.IsAny<int>())).Returns(Constants.CLIENT_TICKET);
//ACT
var result = GetController(context.Object).Index(Constants.TICKET_ID);
var model = ((ViewResult)result).Model;
//ASSERT
Assert.IsInstanceOfType(model, typeof(TicketModel), "ViewModel should have been an instance of TicketModel.");
}
private TicketController GetController(HttpContextBase context)
{
var controller = new TicketController(MockTicketRepository.Object);
controller.ControllerContext = GetControllerContext(context, controller);
return controller;
}
}
Constants.CLIENT_TICKET
public static Ticket CLIENT_TICKET
{
get
{
var ticket = new Ticket
{
CategoryID = 1,
CreatedByUserId = 4
};
ticket.Clients.Add(new Client { ShortName = "Test Client 1"});
ticket.Clients.Add(new Client { ShortName = "Test Client 2" });
ticket.User = new User {FirstName = "First", LastName = "Last"};
return ticket;
}
}
Controller
private readonly ITicketRepository _ticketRepository;
public TicketController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ActionResult Index(int id)
{
var ticket = _ticketRepository.GetById(id);
// etc...
}
Could you show the controller code under test? It could be related to how you have set up the mocked context but it's hard to tell without seeing the controller code.
Also, if you add MockBehavior.Strict when you create the mock, it will bomb out if the invocation doesn't have a corresponding expectation:
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
UPDATE
I've tried to strip everything back so that the test is as simple as possible to try and isolate the issue. Here's what I have come up with:
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository;
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
MockTicketRepository.Setup(x => x.GetById(Constants.TICKET_ID)).Returns(Constants.CLIENT_TICKET);
var controller = new TicketController(MockTicketRepository.Object);
//ACT - try to keep ACT as lean as possible, ideally just the method call you're testing
var result = controller.Index(Constants.TICKET_ID);
//ASSERT
var model = ((ViewResult)result).ViewData.Model;
Assert.That(model, Is.InstanceOfType<TicketModel>(), "ViewModel should have been an instance of TicketModel.")
}
}
I have the following controller action
public void Post(Dto model)
{
using (var message = new MailMessage())
{
var link = Url.Link("ConfirmAccount", new { model.Id });
message.To.Add(model.ToAddress);
message.IsBodyHtml = true;
message.Body = string.Format(#"<p>Click here to complete your registration.<p><p>You may also copy and paste this link into your browser.</p><p>{0}</p>", link);
MailClient.Send(message);
}
}
To test this I need to setup the controller context
var httpConfiguration = new HttpConfiguration(new HttpRouteCollection { { "ConfirmAccount", new HttpRoute() } });
var httpRouteData = new HttpRouteData(httpConfiguration.Routes.First());
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost");
sut = new TheController
{
ControllerContext = new HttpControllerContext(httpConfiguration, httpRouteData, httpRequestMessage),
MailClient = new SmtpClient { PickupDirectoryLocation = location }
};
This seems like a lot of setup to test the creation of a link. Is there a cleaner way to do this? I have read about in-memory servers but that looks like it applies more to the httpclient than testing the controller directly.
I started using this approach with Web API 2.0.
If you're using a mocking library (and you really should for any real world unit tests), you are able to directly mock the UrlHelper object as all of the methods on it are virtual.
var mock = new Mock<UrlHelper>();
mock.Setup(m => m.Link(It.IsAny<string>(), It.IsAny<object>())).Returns("test url");
var controller = new FooController {
Url = mock.Object
};
This is a far cleaner solution than Ben Foster's answer, as with that approach, you need to add routes to the config for every name that you're using. That could easily change or be a ridiculously large number of routes to set up.
Below is the absolute minimum code required to test UrlHelper without any kind of mocking library. The thing that threw me (and took me some time to track down) was that you need to set the IHttpRouteData of the request. If you don't the IHttpRoute instance will fail to generate a virtual path resulting in an empty URL.
public class FooController : ApiController
{
public string Get()
{
return Url.Link(RouteNames.DefaultRoute, new { controller = "foo", id = "10" });
}
}
[TestFixture]
public class FooControllerTests
{
FooController controller;
[SetUp]
public void SetUp()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost");
request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = new HttpRouteData(new HttpRoute());
controller = new FooController
{
Request = request
};
}
[Test]
public void Get_returns_link()
{
Assert.That(controller.Get(), Is.EqualTo("http://localhost/api/foo/10"));
}
}
I'm running into the same idiocy. All the references I can find want you to Mock the Request/Controller, which is (as you pointed out) a lot of work.
Specific references:
http://aspnetwebstack.codeplex.com/discussions/358709/
http://www.peterprovost.org/blog/2012/06/16/unit-testing-asp-dot-net-web-api/#testing-the-harder-stuff-postproduct
ASP.NET MVC Controller Unit Testing - Problem with UrlHelper Extension
ASP.NET MVC: Unit testing controllers that use UrlHelper
the rest of the internet
I haven't gotten around to trying the actual Mocking frameworks, so I have a helper class to "build" my controller. So instead of
sut = new TheController { ... }
I use something like:
// actually rolled together to `sut = MyTestSetup.GetController(method, url)`
sut = new TheController()...
MyTestSetup.FakeRequest(sut, HttpMethod.Whatever, "~/the/expected/url");
For reference, the method is basically:
public void FakeRequest(ApiController controller, HttpMethod method = null, string requestUrl = null, string controllerName = null) {
HttpConfiguration config = new HttpConfiguration();
// rebuild the expected request
var request = new HttpRequestMessage( null == method ? this.requestMethod : method, string.IsNullOrWhiteSpace(requestUrl) ? this.requestUrl : requestUrl);
//var route = System.Web.Routing.RouteTable.Routes["DefaultApi"];
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
// TODO: get from application? maybe like https://stackoverflow.com/a/5943810/1037948
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", string.IsNullOrWhiteSpace(controllerName) ? this.requestController : controllerName } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
// attach fake request
controller.Request = request;
controller.Request.Properties[/* "MS_HttpConfiguration" */ HttpPropertyKeys.HttpConfigurationKey] = config;
}