c# Unit test mock class attribute - c#

In my MVC project I had to setup a controller class attribute:
public class ResponsableAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var controller = (BaseController)filterContext.Controller;
if (!controller.EstResponsable)
{
filterContext.Result = controller.RedirectionForcee(Alertage.Information, ConstantesUi.Misc.MessageResponsable);
}
}
}
Bear with me, the code's in french:
[Responsable]
public class ParamLivraisonController : BaseController
So every action has to pass through the attribute before being executed, which is the intended behavior.
But I do not know how to unit test this attribute, set the return value as false so that the controller.RedirectionForcee (which returns a RedirectToRouteResult) actually does its job.
If I call any controller method:
[TestMethod]
public void ParamLivraisonController_GererLivraison_Get()
{
ControleurAsynchrone.ObtenirLivraisons().Returns(ObtenirListeLivraison());
var retour = _ctrl.GererLivraisons();
Assert.IsNotNull(retour);
}
The attribute is not hit or tested. How can test it? I am using nSubstitute with mvcFakes and I do not know how to substitute ActionExecutingContext.

Thanks to #Kenneth K., I found the answer. #Nkosi was right as well to mention that the test should be on the attribute itself.
Here it is:
[TestMethod]
public void ValiderAttribut_EstResponsable()
{
var attribut = new ResponsableAttribute(); // Instantiation of the attribute
var controller = ObtenirController(); // Gets the controller via MvcFakes
SecuriteHelper.VerifierResponsable().Returns(true); // Sets the desired return value
var test = Substitute.For<ActionExecutingContext>(); // Substitute for ActionExecutingContext
test.Controller = controller; // Sets the controller to the context
attribut.OnActionExecuting(test); // Call the overrided method
Assert.IsNull(test.Result); // Check if the redirection occured
}
Now, to validate if the test was right, I also had to test the opposite result:
[TestMethod]
public void ValiderAttribut_EstNonResponsable()
{
var attribut = new ResponsableAttribute();
var controller = ObtenirController();
SecuriteHelper.VerifierResponsable().Returns(false);
var test = Substitute.For<ActionExecutingContext>();
test.Controller = controller;
attribut.OnActionExecuting(test);
Assert.IsNotNull(test.Result);
Assert.AreEqual(typeof(RedirectResult), test.Result.GetType());
}
And it worked. Thanks!

Related

.net core routing, understand what application called with a generic route

So what I have is a base controller that the following [Route] definition
[Route("{application}/api/[controller]")]
public class BaseController
{
}
All of my current controllers inherit from BaseController.
What I am trying to achieve is that two different application can call my controllers and my code to be aware of what 'application' is calling it.
Application 1 should be able to call /Application1/Api/MyController
Application 2 should be able to call /Application2/Api/MyController
and both requests should go to the same controller but my code should be aware of which application called it.
I thought about having some sort of Middleware and then work out the application from the Request.Path, and then store it in something like HttpContext.Current.Items but that doesn't seem like the correct way to do it.
My personal preference here would be to pass the value as an HTTP header rather than a route parameter, especially if you want it everywhere. It means you don't need a Route attribute and a different URL per application. Using a custom ActionFilterAttribute, there's a bunch of ways you can pass this detail into your action. For example:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Headers.TryGetValue("Application", out var values))
{
// Method 1: This allows you to specify a parameter on your action
context.ActionArguments.Add("application", values.First());
// Method 2: This adds the value into the route data
context.RouteData.Values.Add("Application", values.First());
// Method 3: This will set a property on your controller
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = values.First();
}
}
base.OnActionExecuting(context);
}
}
And apply it to action methods or your controller:
[Application]
public class FooController : Controller
{
}
Method 1 Usage:
public IActionResult Index(string application)
{
// do something with the parameter passed in
}
Method 2 Usage:
public IActionResult Index(string application)
{
var application = (string)RouteData.Values["Application"];
}
Method 3 Usage:
First, create a base controller that contains the property:
public abstract class BaseApplicationController : Controller
{
public string Application { get; set; }
}
Then make sure your controller inherits from it:
[Application]
public class FooController : BaseApplicationController
{
}
Now you can access the property on your controller:
public IActionResult Index(string application)
{
var application = this.Application;
}
Bonus Method 4:
As an aside, you could use this method to use the URL route value, using the base controller from method 3, modify the attribute to look like this:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = (string)context.RouteData.Values["application"];
}
base.OnActionExecuting(context);
}
}
Add a route attribute to your controller:
[Route("{application}/api/[controller]/[action]")]
And now you should have the property value on the controller set.
You could move the route template into action and then each action would technically be aware of its caller context by your proposed convention:
[Route("api/[controller]")]
public class YourController : BaseController
{
[HttpGet("{application}")]
public IActionResult Get(string application)
{
if (application == "Application1")
{
...Application1 called
}
if (application == "Application2")
{
...Application2 called
}
...
}
}
Of course, this is your proposed convention and it is not enforced through some custom application authentication in any way so you will have to trust that your callers will correctly identify themselves through this convention.
Another approach, could be to have a base class variable and set that after inspecting the route.
[Route("{application}/api/[controller]")
public class BaseController: Controller
{
protected string CallingApp { get; set; }
public override void OnActionExecuting(ActionExecutingContext ctx)
{
CallingApp = ctx.RouteData.Values["application"];
base.OnActionExecuting(ctx);
}
}

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

c# .net access parent obj

I am using .net c# MVC controller to query database for many of my projects. Every time i create a new controller, I find myself having to rewrite some of the same function for the new controller hence, I thought about writing a basic controller to handle some of the basic task that I use in all my controller (e.g., run a query and run json).
In my controller, I reference the basic controller like this.
namespace myWebAPI.Controllers
{
public class esrcController : Controller
{
//
// GET: /esrc/
string db = "esrc-";
basicController BasicController = new basicController();
public string test()
{
return "test" + Request.ServerVariables["HTTP_REFERER"];
}
public string getCodingException()
{
return #"{""data"":" + BasicController.getDataNconvertToJSON(
"select * from z_codingexception order by nc_key",
BasicController.getEnviroment(db)) + "}";
}
}
}
in my BasicController, the getEnviroment looks at the url to determine the environment hence I need access to :
Request.ServerVariables["HTTP_REFERER"] and Request.ServerVariables["HTTP_HOST"].ToString().ToLower();
but Request is null in this controller, I only have access to request in the main controller. How do I reference httpRequest from basic controller?
Just because you instantiate a new instance of a controller, doesn't mean you'll have access to the context.
One option is to create an abstract base controller that all of your other controlers would inherhit from. You'll then have access to the specific objects like Request
WebApiConfig.cs
config.MapHttpAttributeRoutes();
Your Controller
public abstract class MyBaseController : Controller
{
protected void myMethod()
{
// you have access to Request here
}
}
public class MyController : MyBaseController
{
[HttpGet]
[Route("my/getstuff")]
public IHttpActionResult GetStuff()
{
// do stuff
base.myMethod();
return Ok();
}
}
Create an action filter and add it as an attribute to that class. Within the action filter yuo wil have access to the Request object. If you override the OnActionExecuting function, the functionality in your filter will be executed before your controller.
Create a custom filter
public class CustomAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//DO STUFF WITH YOUR REQUEST OBJECT HERE..
}
}
Add the filter as an attribute to your controller
[CustomAttribute]
public class esrcController : Controller

How to pass parameters to a custom ActionFilter in ASP.NET MVC 2?

I'm trying to create a custom ActionFilter which operates on a set of parameters that would be passed to it from the controller.
So far, my customer ActionFilter looks like this:
public class CheckLoggedIn : ActionFilterAttribute
{
public IGenesisRepository gr { get; set; }
public Guid memberGuid { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Member thisMember = gr.GetActiveMember(memberGuid);
Member bottomMember = gr.GetMemberOnBottom();
if (thisMember.Role.Tier <= bottomMember.Role.Tier)
{
filterContext
.HttpContext
.Response
.RedirectToRoute(new { controller = "Member", action = "Login" });
}
base.OnActionExecuting(filterContext);
}
}
I know I still need to check for nulls, etc. but I can't figure out why gr and memberGuid aren't successfully being passed. I'm calling this Filter like this:
[CheckLoggedIn(gr = genesisRepository, memberGuid = md.memberGUID)]
public ActionResult Home(MemberData md)
{
return View(md);
}
genesisRepository and md are being set in the controller's constructor.
I'm not able to get this to compile. The error I get is:
Error 1 'gr' is not a valid named attribute argument because it is not a valid attribute parameter type
Error 2 'memberGuid' is not a valid named attribute argument because it is not a valid attribute parameter type
I double checked that gr and memberGuid were the same types as genesisRepority and md.memberGUID, What is causing these errors?
Solution
Thanks to jfar for offering a solution.
Here's the Filter I ended up using:
public class CheckLoggedIn : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var thisController = ((MemberController)filterContext.Controller);
IGenesisRepository gr = thisController.GenesisRepository;
Guid memberGuid = ((MemberData)filterContext.HttpContext.Session[thisController.MemberKey]).MemberGUID;
Member thisMember = gr.GetActiveMember(memberGuid);
Member bottomMember = gr.GetMemberOnBottom();
if (thisMember.Role.Tier >= bottomMember.Role.Tier)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new {
controller = "Member",
action = "Login"
}));
}
base.OnActionExecuting(filterContext);
}
}
This is a way to make this work. You have access to the ControllerContext and therefore Controller from the ActionFilter object. All you need to do is cast your controller to the type and you can access any public members.
Given this controller:
public GenesisController : Controller
{
[CheckLoggedIn()]
public ActionResult Home(MemberData md)
{
return View(md);
}
}
ActionFilter looks something like
public class CheckLoggedIn : ActionFilterAttribute
{
public IGenesisRepository gr { get; set; }
public Guid memberGuid { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
/* how to get the controller*/
var controllerUsingThisAttribute = ((GenesisController)filterContext.Controller);
/* now you can use the public properties from the controller */
gr = controllerUsingThisAttribute .genesisRepository;
memberGuid = (controllerUsingThisAttribute .memberGuid;
Member thisMember = gr.GetActiveMember(memberGuid);
Member bottomMember = gr.GetMemberOnBottom();
if (thisMember.Role.Tier <= bottomMember.Role.Tier)
{
filterContext
.HttpContext
.Response
.RedirectToRoute(new { controller = "Member", action = "Login" });
}
base.OnActionExecuting(filterContext);
}
}
Of course this is assuming the ActionFilter isn't used across multiple controllers and you're ok with the coupling. Another Option is to make a ICheckedLoggedInController interface with the shared properties and simply cast to that instead.
You can only use constant values for attribute properties; see a this page for a full explanation.
Attributes are essentially metadata added to a type. They can only use const values, instead of instance variables. In your case you are tying to pass in your instance variables of genisisRepository, etc. This will fail to compile as they are not compile time constants.
You should look into Dependency Injection for Action Filters to achieve this, typically using an IoC container.
Also, if your ActionFilter is performing a post ActionResult action, such as OnActionExecuted, you could probably get away with storing something in the route data:
public ActionResult Index()
{
ControllerContext.RouteData.DataTokens.Add("name", "value");
return View();
}

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