Asp.NET MVC Arearegistration Route Unit Test With Telerik Just Mock Lite - c#

I am trying to Asp.NET MVC Admin area route unite test with telerik just mock lite.But I cant test.
Here is My Trying Code:
[TestMethod]
public void AdminRouteUrlIsRoutedToHomeAndIndex()
{
//spts.saglik.gov.tr/admin
//create route collection
var routes = new RouteCollection();
var areaRegistration = new AdminAreaRegistration();
Assert.AreEqual("Admin",areaRegistration.AreaName);
// Get an AreaRegistrationContext for my class. Give it an empty RouteCollection
var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes);
areaRegistration.RegisterArea(areaRegistrationContext);
// Mock up an HttpContext object with my test path (using Moq)
var context = Mock.Create<HttpContext>();
context.Arrange(c=>c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/Admin");
// Get the RouteData based on the HttpContext
var routeData = routes.GetRouteData(context.Request.RequestContext.HttpContext);
//assert has route
Assert.IsNotNull(routeData,"route config");
}
When var context = Mock.Create<HttpContext>(); just mock tells this error
Telerik.JustMock.Core.ElevatedMockingException: Cannot mock 'System.Web.HttpContext'. JustMock Lite can only mock interface members, virtual/abstract members in non-sealed classes, delegates and all members on classes derived from MarshalByRefObject on instances created with Mock.Create or Mock.CreateLike. For any other scenario you need to use the full version of JustMock.
So How can I do area registration route unit test with telerik just mock lite? How can I Solve this issue?
Thanks a lot.

HttpContext is something you can't mock. The data it contains is specific to a particular request. So in order to run a test using HttpContext, you'll have actually run your application in an environment that you can make requests to.
Instead, you'll need to use a third-party tool like MvcRouteTest (https://github.com/AnthonySteele/MvcRouteTester). It's easy to use, and most importantly, you can run the tests without running your app.
[TestMethod]
public void AdminRouteUrlIsRoutedToHomeAndIndex()
{
var routes = new RouteCollection();
var areaRegistration = new AdminAreaRegistration();
Assert.AreEqual("Admin", areaRegistration.AreaName);
var areaRegistrationContext = new AreaRegistrationContext(areaRegistration.AreaName, routes);
areaRegistration.RegisterArea(areaRegistrationContext);
routes.ShouldMap("/admin").To<HomeController>(r => r.Index());
}
This tests both the area registration and the route for the /admin URL (some would argue that it should be broken into two tests). It assumes that your AdminAreaRegistration's RegisterArea() method builds the default route with the default controller set as Home:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller="home", action = "Index", id = UrlParameter.Optional }
);
}

Related

How to mock the controller

I have a method in a controller that check's the user's role to see which page the user should be redirected.
I am trying to do unit testing for the following code
public class HomeController: Controller {
//...
public IActionResult Home()
{
if (User.IsInRole("Administrators")
{
return RedirectToAction("/administrator");
}
else
{
return RedirectToAction("/user");
}
}
//...other actions
}
That mock setup is incorrect; you actually cannot mock members of concrete classes (unless they're virtual). But thankfully that's no big deal here, because you don't need to use a mock. Generally speaking the following is a working way of dealing with HttpContext testing:
[Test]
public async Task HomeReturnsNotNull()
{
var controller = new HomeController();
var controllerCtx = new ControllerContext()
{
HttpContext = new DefaultHttpContext()
{
User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Role, "Administrators") }))
}
};
controller.ControllerContext = controllerCtx;
ActionResult index = (ActionResult)controller.Home();
Assert.IsNotNull(index);
}
But it's often more comfortable to rely on some abstractions of your own, instead of calling HttpContext members directly.
Took the liberty to change 'admin' to 'Administrators', because I noticed a discrepancy between the value in the test and the value in the controller action.
This way the IsInRole() check in the controller action should execute properly, which should be useful in the other tests (since in this one it seems you're just checking against null).

How to test methods with attributes with asp.net core 2?

My project is an ASP.NET Core 2.0 web application.
How can I test if my own written attribute is working fine by calling a method containing the attribute?
For example:
Attribute
[AttributeUsage(AttributeTargets.Method)]
public class ValidateUserLoggedInAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var controller = (BaseController)context.Controller;
if (!controller.UserRepository.IsUserLoggedIn)
{
var routeValueForLogin = new RouteValueDictionary(new { action = "Login", controller = "Home", area = "" });
context.Result = new RedirectToRouteResult(routeValueForLogin);
}
}
}
Controller:
[ValidateUserLoggedIn]
public IActionResult Start()
{
...
}
Test:
[TestMethod]
public void Test()
{
// Act
var result = this.controllerUnderTest.Start() as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result);
Assert.IsFalse(result.Permanent);
var routeValues = result.RouteValues;
const string ControllerKey = "controller";
Assert.IsTrue(routeValues.ContainsKey(ControllerKey));
Assert.AreEqual("Home", routeValues[ControllerKey]);
const string ActionKey = "action";
Assert.IsTrue(routeValues.ContainsKey(ActionKey));
Assert.AreEqual("Login", routeValues[ActionKey]);
}
I have already written a test only for the attribute by creating an ActionExecutingContext, but I also want to test it for the controller method.
That would require an integration test where you send an actual request either with an in-memory test server or running site and then verify expected behavior.
Refer to Integration testing in ASP.NET Core
You you have already tested the attribute in isolation with a unit test, it stands to reason that it would behave as expected in an integration and in production provided the test covered expected behavior

Mocking a controller to test ViewEngine inside an Area - nullreference and RouteData

I have an Area in my MVC site. This area has the typical Controller/Model/View setup.
As a controller I have the following code:
public class DocumentCreatorController : Controller
{
// GET: Templates/DocumentCreator
public ActionResult OfferTemplate(BaseDocumentViewModel data)
{
return this.Pdf(nameof(OfferTemplate), data, "File.pdf");
}
}
The method this.Pdf does a couple of stuff, but the interesting is it comes down to the ViewEngine call:
var viewResult = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);
Here I call the FindPartialView with a ControllerContext and a PartialViewName. My PartialViewName comes from the nameof(OfferTemplate) from the controller action OfferTemplate. I think the controllercontext is my challenge.
My challenge:
When I want to set this up in a unit test (using Moq), I have the following code based on pages such as Mocking The RouteData Class in System.Web.Routing for MVC applications and Mocking Asp.net-mvc Controller Context:
[TestMethod]
public void OfferTemplate()
{
var ctr = SetupControllerWithContext();
}
private static DocumentCreatorController SetupControllerWithContext()
{
var routeData = new RouteData();
routeData.Values.Add("controller", "DocumentCreatorController");
routeData.Values.Add("action", "OfferTemplate");
var request = new Mock<HttpRequestBase>();
request.Expect(r => r.HttpMethod).Returns("GET");
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Expect(c => c.Request).Returns(request.Object);
var controllerContext = new ControllerContext(mockHttpContext.Object
, routeData, new Mock<ControllerBase>().Object);
DocumentCreatorController ctr = new DocumentCreatorController();
ctr.ControllerContext = controllerContext;
return ctr;
}
Which gives the following error:
Eesy.Websites.Api.Tests.Controllers.DocumentCreatorControllerTest.OfferTemplate
threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
This I don't understand.
My folder setup:
Debug image on ControllerContext on calling the FindPartialView:
Anyone have an idea?
Is it because I setup the RouteData wrong?
You are trying to mock and test framework code. Abstract that functionality out into code you control so you can test in isolation if needed.
Currently the action and by extension the controller is tightly coupled to external 3rd party dependencies. If the goal was to test the controller action flow in isolation then it is advised to abstract out the 3rd party PDF generation so that it can be mocked for easier testability.
public interface IDocumentService {
ActionResult ToPdf(Controller arg1, string arg2, object arg3, string arg4);
}
The controller would explicitly depend on this abstraction via constructor injection.
public class DocumentCreatorController : Controller {
private readonly IDocumentService render;
DocumentCreatorController(IDocumentService render) {
this.render = render;
}
// GET: Templates/DocumentCreator
public ActionResult OfferTemplate(BaseDocumentViewModel data) {
return render.ToPdf(this, nameof(OfferTemplate), data, "File.pdf");
}
}
So now to test the controller's pdf generation process you need only mock your abstraction.
[TestMethod]
public void OfferTemplate() {
//Arrange
var serviceMock = new Mock<IDocumentService>();
//...setup mock for use case
var controller = new DocumentCreatorController(serviceMock.Object);
var data = new BaseDocumentViewModel {
//...
};
//Act
var actual = controller.OfferTemplate(data);
//Assert
//...assert behavior
}
The actual implementation of the service would encapsulate the actual functionality and would be registered with the dependency injection container along with the abstraction.
To test the actual generation you would need to do an integration test which is another topic.

Unit Testing ViewResult in Asp.NET MVC

Even though there are couple of Posts on StackOverflow about Unit Testing Action Result in MVC, I have a specific Question ....
Here is my ActionResult in Controller:
public ActionResult Index()
{
return View(db.Products.ToList());
}
Every Item in Products has different attributes like Name,Photo,Quantity etc..
I wrote a testmethod for this method .It looks as follows :
private CartEntity db = new CartEntity();
[TestMethod]
public void Test_Index()
{
//Arrange
ProductsController prodController = new ProductsController();
ViewResult = prodController.Index();
}
What Should I compare in this case since there are no parameters are being passed into Index Action
Check out the ViewResult class, this can show you what else you could test.
What you need to do is mock your DbContext and supply it with data in the Products property (DbSet<>) as this is being called in your controller's action.
You can then test
The type being returned
The model on the ViewResult
The ViewName which should be empty or Index
Sample code
[TestMethod]
public void Test_Index()
{
//Arrange
ProductsController prodController = new ProductsController(); // you should mock your DbContext and pass that in
// Act
var result = prodController.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model); // add additional checks on the Model
Assert.IsTrue(string.IsNullOrEmpty(result.ViewName) || result.ViewName == "Index");
}
If you need help mocking a DbContext there are existing frameworks and articles on this subject. Here is one from Microsoft titled Testing with a mocking framework. Ideally you should be injecting your dependencies (including DbContext instances) into the constructors of your Controller instances using a DI framework like AutoFac or Unity or NInject (the list goes on). This also makes unit testing much easier.

ASP.NET 5/MVC 6 pipeline branching and controller namespace constraint

I would like to use pipeline branching in ASP.NET 5/MVC 6 in such a way that only controllers in namespace xxx.yyy.Pipe1.Controllers are "available" to a branch mapped like this in 'Startup.cs' app.Map("/pipe1", ConfigurePipe1);
What would be the preferred, and/or correct, way to do that?
It is not so much about controller discovery as about restricting the set of controllers that can be resolved during request processing. My reason for doing this is the need to use different authentication schemes per pipeline, and thereby per set of controllers.
Thanks!
If I understood you correctly you want to map applications by the corresponding controller's namespace?
I think this is not possible. There is MapWhen method. I tried to resolve the controller when it invoked but I had no luck (I knew it was hopeless).
app.MapWhen(context => {
var shouldWeMap = ... // here I tried many things but it was impossible to resolve the controller.
return shouldWeMap;
}, ConfigurePipe1);
If you want to learn which controller will be hit, you have to let asp.net map this request to a mvc configuration. But after doing this, you miss your chance to map that request to an app as already having done it :(
I found a workaround. It uses a custom IActionFilter to check which configuration is used in every request.
Just map a new configuration as you suggested before:
app.Map("/pipe1", ConfigurePipe1);
And then in ConfigurePipe1 make MVC to MapRoute with a different signature (unique name?). With this way you can implement your own global IActionFilter and force it to check which RouteData has been used.
And there you can do whatever you want. Check controller's namespace and so on...
So ConfigurePipe1 may be like:
public void ConfigurePipe1(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "CustomPipeRoute",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
});
}
And in custom IActionFilter we can check them like:
public class GlobalActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
var controllerNamespace = context.Controller.GetType().Namespace;
var pipeRoute = context.RouteData.Routers.OfType<TemplateRoute>().FirstOrDefault(x => x.Name == "CustomPipeRoute");
if (pipeRoute != null)
{
// We are using /pipe1
}
if (.....)
{
// You can redirect to somewhere else if you want.
var controller = (Controller)context.Controller;
context.Result = controller.RedirectToAction("Index", "Controller");
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
Also don't forget to register the custom filter as:
services.AddMvc(config =>
{
config.Filters.Add(typeof(GlobalActionFilter));
});

Categories