MVC Core Get Controller ViewModel Results - c#

I am trying to get result from Controller which calls Repository which extracts a ViewModel at the end, this is for Unit testing. The bold below is not working.
Compile error:
IAction Result does not contain a definition for View and no extension method View accepting a first argument of type IAction Result
public async Task<IActionResult> Search(int? id)
{
return View(await _repository.GetProductData(id));
}
var searchresult1 = await _controller.Search(5);
var modelresult = searchresult1.View.ProductName;

try something like this
var searchresult1 = await _controller.Search(5) as ViewResult;
var result = searchResult.ViewName; // this should be the value returned from the controller
Or you can return the result as a ViewData.Model. So in your controller, you can probably do something like
return View("Product", await _repository.GetProductData(id));
Then in your test you can access the viewdata.model like
var result = await _controller.Search(5);
var productData = (ProductData) result.ViewData.Model; // cast it to whatever class the GetProductData(5) returns
Option 1 works at run time as the view name is set by the framework using route data. it is not available during unit testing. Which is why setting the view name manually in the second example works
Please see more details about MVC view model

Related

How can I setup a multi-item list with moq

I'm trying to write a unit test for my method but failed. I want to return the list with the 2 item response of GetMyRequest with Mock(package Moq), then I run Task.WhenAll with the response returned with my method. So I used the SetupSequence method but it returned a single-item list. How can I return a multi-item list in my test.
public void MyMethod()
{
ProductService = new Mock<IProductService>();
var myResponse = myStringList.Select(async x => await _myService.GetMyRequest(x, null, null)).ToList();
//my response type -> List<Task<ResponseModel>>
var myResponses = await Task.WhenAll(myResponse);
}
I want myResponses multi-item list with mocking but it single-item.
Unit tests
public async Task InitializeAsync(){
ServiceResponse = Fixture.Build<MyModel>().CreateMany(2).ToList();
MyService = new Mock<IProductService>();
MyService.SetupSequence(service => service.GetMyRequest(It.IsAny<string>(), null, null))
.ReturnsAsync(ServiceResponse[0])
.ReturnsAsync(ServiceResponse[1]);
}
TL; DR: You can't return multiple items for your GetMyRequest
SetupSequence means that you want to setup your GetMyRequest in a way that multiple calls against it will result with different responses.
For the first call you will receive the value of ServiceResponse[0]
For the second call you will receive the value of ServiceResponse[1]
According to my understanding the return type of GetMyRequest is Task<ServiceResponse> , that's why you can't define a mock to return Task<IEnumerable<ServiceResponse>> or something similar.

NSubstitute returning empty string

I have the following method under test:
public HomeController(IUserIpAddressHelper userIpAddressHelper)
{
_userIpAddressHelper = userIpAddressHelper;
}
[HttpGet]
public ActionResult Index()
{
var userIpAddress = _userIpAddressHelper.GetUserIpAddress(System.Web.HttpContext.Current);
if (_userIpAddressHelper.IsIpAddressOddOrEven(userIpAddress))
{
return RedirectToAction(HomePage);
}
return RedirectToAction(HomePageAlternative);
}
and I am testing as follows:
public void Test()
{
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
var controller = new HomeController(userIpAddressHelper);
var result = controller.Index();
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}
However the test is failing due to the value of userIpAddress being "" an empty string, instead of 0.0.0.2 as I've set it. Can anyone please point out where I've gone wrong here?
Is userIpAddress definitely ""? It looks like the Returns in your original test is specified well, but if IUserIpAddressHelper is an interface then the substitute for it will not have a result stubbed for IsIpAddressOddOrEven, so it will always return false even if GetUserIpAddress is stubbed to return "0.0.0.2".
To get the test to mirror how the production code passes through the data, you can stub out both members:
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
userIpAddressHelper.IsIpAddressOddOrEven("0.0.0.2").Returns(true);
This will test that the production code correctly passes through the result of GetUserIpAddress to IsIpAddressOddOrEven.
Note: we could also stub these to work with "ip-address-result" and it would still work. We don't need a valid odd/even result returned, as we are not using a real implementation of IUserIpAddressHelper, just a substitute for testing. If you find it necessary to substitute for IUserIpAddressHelper in lots of tests and you want it to act like a real implementation (i.e. it will actually return whether an address is odd or even), it might be easier to write a TestUserIpAddressHelper.
Another way to avoid having the dependency between the results of GetUserIpAddress and IsIpAddressOddOrEven is to change the interface to have a bool IsIpAddressOddOrEven(HttpContext context) method that combines both operations. That way you would only need to stub one for the test.
If you have problems with System.Web.HttpContext.Current, you can try to mock IsIpAddressOddOrEven method instead. They will both do the same job for your test.
Like this:
public void Test()
{
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.IsIpAddressOddOrEven(Arg.Any<string>()).Returns(true);
var controller = new HomeController(userIpAddressHelper);
var result = controller.Index();
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}

How do I get dynamic properties from OkObjectResult

I return IActionResult with the value of an anonymous object from a controller method.
How do I get the data out again?
Here is the controller method reduced to the very problem:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
long size = file.Length;
return Ok(new { count = 1, size });
}
Then I get the .Value property, which is an object and make it dynamic.
But no, it is not recognised.
[Fact]
public async Task UploadFileTest()
{
// # Given.
var formFile = new Mock<IFormFile>();
var sut = new HomeController(null);
// # When.
IActionResult result = await sut.UploadFile(formFile.Object);
// # Then.
OkObjectResult okResult = (OkObjectResult)result; // Ok.
dynamic dynValue = okResult.Value; // Ok.
var count = dynValue.count; // Not ok.
}
Expected result:
count should be 1.
Actual result:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : 'object' does not contain a definition for 'count'
Try reflection:
var count = (int)okResult.Value.GetType().GetProperty("count").GetValue(okResult.Value);
If your test and original code not in the same project, If that so, you need to create AssemblyInfo.cs in your original code project to make it (original) share the dynamic object structure.
you could see more at https://sodocumentation.net/csharp/topic/4264/assemblyinfo-cs-examples#-internalsvisibleto-
It shall has code like below (Replaced the string as your test name)
[assembly: InternalsVisibleTo("MyAssembly.UnitTests")]

asp.net core OkObjectResult and IActionResult with unit test are not letting me loop over List - nested Result.Value

I am trying to write a unit test with asp.net core in which the code I have looks like the following
[TestMethod]
public void NotificationController_should_return_notifications()
{
//Act
var controller = new NotificationsController(_dbContext, _logger);
IActionResult actionResult = controller.Get();
OkObjectResult okObjectResult = actionResult as OkObjectResult;
var model = okObjectResult.Value; //as List<Notification>;
When others do NOT have a paging object nested, they are able to just do
var model = okObjectResultValue as TrainingMessages;
However, with my complex object, the model ends up being null as the collection is too complex with paging etc...
What I want to do it just to get at the data that I SEE in the Watch
okObjectResult.Value.Results[0].Id
but this is not available to me, why?
Here is the picture of the code and the WATCH
You have a nested Result.Value and its most likely because you're doing this:
My Create method calls the GetDashboard to return the newly inserted Dashboard. However, I'm returning an OK(result) to an OK. So its returning (Ok(Ok(result)).
Solution (only return one Ok):
return await GetDashboard(dashboardId);

How do I Unit Test the correct view is returned with MVC ASP.Net?

I’m new to MVC, Unit Testing, Mocking and TDD. I’m trying to follow best practice as closely as possible.
I’ve written a unit test for a controller and I’m having trouble testing if the correct view is returned. If I use the ViewResult.ViewName the test always fails if I don’t specify the view name in the controller. If I do specify the ViewName in the controller the test always passes, even if the view doesn’t exist.
I’ve also tried testing the Response.Status code however this always returns 200 (code taken from Darin Dimitrov’s answer to MVC3 unit testing response code). What I’m aiming for is the classic red, green refactor when creating a new view and avoiding 404 and System.InvalidOperationException errors when going live, is this possible?
Code Below.
public class BugStatusController : Controller
{
public ActionResult Index(){
return View(); // Test always fails as view name isn’t specified even if the correct view is returned.
}
public ActionResult Create(){
return View("Create"); // Test always passes as view name is specified even if the view doesn’t exist.
}
}
[TestFixture]
public class BugStatusTests
{
private ViewResult GetViewResult(Controller controller, string controllerMethodName){
Type type = controller.GetType();
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
object instance = constructor.Invoke(new object[] {});
MethodInfo[] methods = type.GetMethods();
MethodInfo methodInfo = (from method in methods
where method.Name == controllerMethodName
&& method.GetParameters().Count() == 0
select method).FirstOrDefault();
Assert.IsNotNull(methodInfo, "The controller {0} has no method called {1}", type.Name, controllerMethodName);
ViewResult result = methodInfo.Invoke(instance, new object[] {}) as ViewResult;
Assert.IsNotNull(result, "The ViewResult is null, controller: {0}, view: {1}", type.Name, controllerMethodName);
return result;
}
[Test]
[TestCase("Index", "Index")]
[TestCase("Create", "Create")]
public void TestExpectedViewIsReturned(string expectedViewName, string controllerMethodName){
ViewResult result = GetViewResult(new BugStatusController(), controllerMethodName);
Assert.AreEqual(expectedViewName, result.ViewName, "Unexpected view returned, controller: {0}, view: {1}", CONTROLLER_NAME, expectedViewName);
}
[Test]
[TestCase("Index", "Index")]
[TestCase("Create", "Create")]
public void TestExpectedStatusCodeIsReturned(string expectedViewName, string controllerMethodName)
{
var controller = new BugStatusController();
var request = new HttpRequest("", "http://localhost:58687/", "");
var response = new HttpResponse(TextWriter.Null);
var httpContext = new HttpContextWrapper(new HttpContext(request, response));
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
ActionResult result = GetViewResult(controller, controllerMethodName);
Assert.AreEqual(200, response.StatusCode, "Failed to load " + expectedViewName + " Error: " + response.StatusDescription);
}
}
I’m new to MVC, Unit Testing, Mocking and TDD. I’m trying to follow best practice as closely as possible.
I feel happy that more and more developers are starting to write unit tests for their code, so congratulations you are on the right path.
if I don’t specify the view name in the controller. If I do specify the ViewName in the controller the test always passes, even if the view doesn’t exist.
When you do not specify a view name in the View method this instructs the MVC engine to render the default view, so for example
public ActionResult Index() { return View(); }
The above code will return an empty view name meaning that the rendered view will be the name of the action, in this case it will be Index.
So if you want to test that an action returns the default view, you have to test that the returned view name is empty
Test always passes as view name is specified even if the view doesn't exist.
In order to explain what's going on here, I'll explain first how the action filters work.
There are basically four types of filters
Exception filters
Authorization filters
Action filters
Result filters
I will concentrate on action and result filters
Action filters are defined with the IActionFilter interface
public interface IActionFilter
{
// Summary:
// Called after the action method executes.
//
void OnActionExecuted(ActionExecutedContext filterContext);
//
// Summary:
// Called before an action method executes.
//
void OnActionExecuting(ActionExecutingContext filterContext);
}
Result filters are defined with the IResultFilter interface
public interface IResultFilter
{
// Summary:
// Called after an action result executes.
//
void OnResultExecuted(ResultExecutedContext filterContext);
//
// Summary:
// Called before an action result executes.
//
void OnResultExecuting(ResultExecutingContext filterContext);
}
When a controller's action is executed the following filters are executed in this specific order:
IActionFilter.OnActionExecuting
IActionFilter.OnActionExecuted
IResultFilter.OnResultExecuting
IResultFilter.OnResultExecuted
When an action is executed, another component is in charge to process your ActionResult returned from your action and render the correct HTML to send it back to the client, this is when the result is processed
This clean separation of concerns is the beauty and the key to allow us unit test our controller's actions, otherwise, if they were coupled, we won't be able to unit test in isolation the result of the actions
Now the RazorViewEngine tries to find a view after an action has been executed (when the result is being processed) that's why your tests return true even when the physical view does not exist. This is the expected behavior, and remember you need to test in isolation your controller's actions. As long as you assert in your unit tests that the expected view is rendered you are done with your unit tests.
If you want to assert that the physical view exists then you would be talking about some specific integration tests: functional tests or user acceptance tests - these kind of tests require the instantiation of your application using a browser they are not in any way unit tests
Now it's OK that you are writing your unit tests manually (this is a great exercise if you are getting into the unit testing world), however, I'd like to recommend you a couple of MVC Testing frameworks that can help you write your unit tests really fast
https://github.com/robdmoore/FluentMVCTesting
http://mvccontrib.codeplex.com/wikipage?title=TestHelper&referringTitle=Documentation
A few personal comments about these frameworks
According to my experience, MVC Contrib has more features than Fluent MVC Testing, however, since I'm using MVC 4 I have not been able to get it working in Visual Studio 2012, so I use a combination of both (this is a dirty workaround until I find a better approach)
This is what I do:
var testControllerBuilder = new TestControllerBuilder(); // this is from MVC Contrib
var controller = new MoviesController(
this.GetMock<IMovieQueryManager>().Object);
testControllerBuilder.InitializeController(controller); // this allows me to use the Session, Request and Response objects as mock objects, again this is provided by the MVC Contrib framework
// I should be able to call something like this but this is not working due to some problems with DLL versions (hell DLL's) between MVC Controb, Moq and MVC itself
// testControllerBuilder.CreateController<MoviesController>();
controller.WithCallTo(x => x.Index(string.Empty)).ShouldRenderDefaultView(); // this is using Fluent MVC Testing
// again instead of the above line I could use the MVC Contrib if it were working....
// var res = sut.Index(string.Empty);
// res.AssertViewRendered().ForView("Index");
I hope this helps =) Happy coding !

Categories