How to set User in MVC 6 Controller test - asp.net-mvc

I have Unit tests for MVC6 controllers where the controller has the [AllowAnonymous] attribute. However a number of my controllers do not have this attribute and I am unable to test those controllers. Also some controllers which allow anonymous access return different data sets depending upon whether the user is authenticated or anonymous.
I have not been able to determine how to set the User details for use in a test and am beginning to think it may not be possible. I hope this is not the case.

Jerrie Pelser posted this exact issue in his blog in Oct. However since then the framework has changed and it 'almost' works. I did find a solution to the problem and posted it in the comment on his blog.
http://www.jerriepelser.com/blog/unit-testing-controllers-aspnet5
Basically read the blog and my comments at the end, then create your controller like this.
var controller = new MyController()
{
ActionContext = new ActionContext
{
HttpContext = httpContext
},
Url = new UrlHelper(),
TempData = Mock.Of<ITempDataDictionary>()
};

Related

How to Use ViewData and ViewBag with Umbraco Surface Controllers

I have just spent 2 hours trying to work out why when I put a string in to View.Bag/ViewData inside my Surface controller, when I try and get the string back in the view I get null.
In the end I have solved the problem by putting the string in to a session variable insted.
Would like to know though why it wasn't working, and how to fix it.
Thanks in advance.
Update: Are you posting and redirecting? When you refresh the form does it prompt you about posting again? If not, it's because you have accidentally followed the best practice of 302ing from a form post (prevents a user refreshing and reposting form data). The examples I was following for login surface controllers all used return RedirectToCurrentUmbracoPage() which I blindly followed. But, as the name implies that really is doing a redirect and it is really two requests! (I stubbornly had to verify in Fiddler before I believed it). ViewData and ViewBag are only good for one request--so they are fundamentally broken in a POST 302. Session is good for multiple requests which is why it worked for you. TempData will work for you too, because as it turns out, TempData is a construct that is built on top of session and was specifically designed to carry state between two posts (removed on retrieve). I read somewhere that TempData would have been better named RedirectData and that helped it click for me.
So when you're dealing with Surface Controllers and POSTing you have three options that I know work:
Session (which you proved worked)
TempData (which is built on session, and from what I've read is both best practice and built specifically for this situation)
Use return CurrentUmbracoPage(); in your form post. I just verified in Fiddler that this is exactly one request (refreshing in the browser prompts a repost warning). I also verified that ViewData works this way. But, because the surface controller is rendered as a Child Action using #Html.Action(...) you have to use ParentActionViewContext to get at the right ViewData (my first answer which I'll leave for others that find this question).
Original answer is still useful when there is no redirect involved (GET or a POST that returns CurrentUmbracoPage())...
In many cases you're actually making a child action. Usually you're only one level deep but if you mix macros and partials you can actually get multiple levels deep. There is a ViewData for each level and you have to walk your way up the stack with ParentActionViewContext to get to the top ViewData that you populated in your controller.
See this comment from Shannon in answer to a question about surface controllers and viewdata (Shannon is a core contributor on the HQ team and has a lot of great content out there). Quoting here:
If you want to access the ViewData that you've set on the master ViewContext's on a ChildAction being rendered from the master's ViewContext then you need to use #ViewContext.ParentActionViewContext.ViewData["ErrorMessage"]
The ParentActionViewContext in this example is the ViewContext that is rendering the Umbraco template, not the ChildAction. That is because when you POST (whether inside of Umbraco or normal MVC), you are posting to a new Action and the rendering process starts from scratch, when you validate your model, update the ViewData, etc... this all happens on what will become the 'master' ViewContext when the view renders. This view then will render your ChildAction.
Twamley's answer above is excellent, in addition to this, I have found that using TempData.Add(key, value) works nicely.
An bare bones would look like:
SurfaceController
public class MyController : Umbraco.Web.Mvc.SurfaceController
{
public MyController()
{}
public ActionResult DoSomething()
{
// surface controller does something
// get a page by it's document/model type alias
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
var node = umbracoHelper.TypedContentSingleAtXPath("//" + "Home")
TempData.Add("Message", "This value will be passed through");
return redirectToUmbracoPage(node);
}
}
View
#inherits UmbracoTemplatePage
#{
Layout = null;
}
#if (TempData.ContainsKey("Message"))
{
<p>#TempData["Message"]</p>
}
http://localhost/umbraco/Surface/My/DoSomething

How do I pass the userId into the model ASP.NET MVC?

I've had a thorough search around but really can't find anything addressing the scenario I'm facing (oddly because I'd have thought it's quite a common thing to do).
Background
I'm creating an application with ASP.NET MVC 4 and Entity Framework 5 Code First. For the purpose of this question, think of it as a blogging application with posts and users.
Project
The post model requires that every post have a corresponding UserId.
With the ASP.NET MVC 4 Membership it is easy to find the username of the person logged in with
User.Identity.Name.
This isn't ideal, we want the ID, but a query such as this can search the db for the name and get the ID.
db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
Problem
The problem arises when trying to create a post. Model.IsValid is false, as no UserId is being passed in from the view. Obviously, as the user isn't expected to enter their ID.
I've tried putting the ID value into the ViewBag and using a #Html.Hidden() field in the view, however I've had no success with this. Model.IsValid always returns false.
Should this information be input through the create view? Or should it be done directly in the controller? Its quite a frustrating problem as I have the information and just need to figure how to pass it into the model.
CONTROLLER CODE
This is basically just the default scaffolded code. The commented code is how I tried setting the model value directly from the controller, however that was little more than trial and error.
//
// POST: /Post/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
//var userId = db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
//post.User.UserId = userId.UserId;
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
Be careful with hidden fields. Anyone could put whatever value they want in that field (i.e. they could spoof another user). You'd be better off caching the ID in the session at login, and using that value.
This is a typical case where you want to create an EditModel as a data transfer object (DTO) between your view and controller layers.
Create a class BlogPostEditModel that has all properties you need the user to fill in when creating a new blog post. Then, map this type (e.g. using AutoMapper) to your BlogPost entity, and fill in the user ID as well.
To use built-in validation such as Model.IsValid(), put the data annotations attributes on the DTO instead.
Honestly, I would have the value assigned via the controller. If you had someone messing with your html via Firebug, they could actually change the id before it was passed and submitted to your form. I would remove it from your Create view and submit from the controller.

TDD'ing MVC Controllers to drive design

I am starting out on a new project (well, restarting an existing one), and trying to adopt TDD (for the nth time) for all the benefits that it should bring.
I believe that TDD will result in my tests driving me to write only the code that I need to write, but it will drive me to write the code that I NEED and not leave some out.
This is where my current state of uncertainty comes in.
Consider the story:
"A user must be able to add a widget, doing so they are taken to view the details of the newly added widget."
OK, so working from the UI (as that's where a user will add their widget from, and not using Visual Studio and a set of assemblies that I write)... I begin with the following test, writing the very minimal so that the test passes.
So I started out with the controller throwing a NotImplementedException, then returning a View()... the following was the first point that I had written the fewest lines I could to make the test pass.
[TestFixture]
public class WidgetControllerTester
{
[Test]
public void Create_IfBusinessModelIsValid_ReturnRedirectToRouteResultToDetailsAction()
{
// Arrange
var currentUser = new User
{
DisplayName = "Fred",
Email = "fred#widgets.com",
Password = "pass",
Status = UserStatus.Active
};
var model = new WidgetModel();
var controller = new WidgetController();
// Act
var actionResult = controller.Create(currentUser, model);
// Assert
actionResult.AssertActionRedirect().ToAction("Details");
}
}
public class WidgetModel
{
}
public class WidgetController: Controller
{
public ActionResult Create()
{
return View("Create");
}
[HttpPost]
public ActionResult Create(User currentUser, Widget model)
{
return RedirectToAction("Details");
}
}
Now I realise that additional tests for invalid models and checking of model state will evolve from additional stories.
However I can't see a clear path of how I would leverage additional tests to drive further code within the controller.
For instance, I know at some point I will want to make a WidgetService call from the Create action. Am I missing something obvious (not being able to see the wood for the trees kind of stuff) how I can progress the controller code with additional tests?
Talking about the WidgetService, I expect that I will write a WidgetServiceTester and for th e time being references within the controller will most likely be mocked.
Some thoughts I have had...
Create a new test called Create_IfModelIsValid_WidgetIsAddedToRepository, but how does this clearly lead onto service calls in the controller action?
I need to write a more detailed story stating that the model needs to be inserted into the repository/database, etc?
Am I muddling up elements of TDD and XP?
Thanks for reading, I would appreciate any feedback and insights to the best practice for progressing.
Joe.
EDIT 27 Feb 2010
I found the following article Iteration #6 – Use Test-Driven Development (on asp.net) (http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-31-cs.aspx) which demonstrates the sort of thing I was after, however they seem to consider addition of repository/service to the controller as re-factoring... I personally don't agree, am I wrong? :)
I'm going to think about writing a test that checks the ViewData of the Details action and update this question once I have.
Joe. I feel a lot of the same uncertainty, sometimes. But I also think that most of what you're missing is those up-front stories. First, you should decompose your story just a tad to create the actual developer requirements. Here's what I mean:
"A user must be able to add a widget, doing so they are taken to view the details of the newly added widget."
Ok, so to me, that breaks out questions as follows, which can help you think of tests:
"A user" -- where did the user come from? How are they logged in? (if you're using the default AccountController and tests, then this is already there, if not, you'll want tests for getting the login form, logging in, validating both successful and failed logins, etc)
"add a widget" -- to what (I don't mean implementation, I just am pointing out that this implies that you're either going to hit a repository or a service, unless 'add' just means add it to the running instance and you don't need persistence)? does the widget have to be valid at this point, or can invalid widdgets be saved and made valid later? This implies to me tests that a repository or service has a method hit (save(), insert(), add(), whatever (not the internals of the method, until you get to testing your service/repo, just that the controller does its job by calling it), check what happens on a valid/invalid widget (you need to expand your story a bit or add a story to cover what should happen on valid/invalid widgets)
"doing so they are taken to view the newly added widget's details" -- reworded slightly, but basically what you said. Always? or only on success? Is this view editable or read-only (i.e. Edit action or Details action)? Is there a message to the user that tells them that they've been successful, or should they infer from the fact that they're viewing their widget that they were successful? This should drive tests that do things like check properties on the returned actionresult and check the values stored in TempData (status message) as well as checking what happens in both paths (success or failure).
that's just a quick shot, but basically that's the thought process. you can also do the same w/ other stories, and for that matter generate new stories to cover more application behavior.
A couple thoughts on your design going forward.
Your next test should look at what I wrote above first, which is the fact that the controller's create POST action should 1) receive needed data (your params), 2) call that service/repository to "add" the widget, 3) possibly do something if that add fails (this is in your design; I've gotten to where my controllers assume all will go well and I handle failures through attributes, but thats' a personal design decision), 4) redirect to details.
So, your next test would be using a mock (I prefer the moq library on google code, but whatever you have will work). You'll need some interface to describe the service your controller will call, and you pass in a mocked implementation of that to your controller under test in order to ensure that it's calling the correct method. In Moq, that'd look something like this:
[Test]
public void Create_CallsRepository()
{
// Arrange
var currentUser = new User
{
DisplayName = "Fred",
Email = "fred#widgets.com",
Password = "pass",
Status = UserStatus.Active
};
var model = new WidgetModel();
var mockService = new Mock<IService<WidgetModel>();
mockService.Setup(s=>s.Add(model)); //.Returns(whatever) if it returns something
var controller = new WidgetController(mockService.Object);
// Act
var actionResult = controller.Create(currentUser, model);
// Assert
mockService.Verify(s=>s.Add(model));
}
That makes some design assumptions, of course, but the tension of how to write your tests vs how your objects should be called / handle things is part of what makes TDD so valuable.

ASP.net MVC - request-scoped global variable

I have a value which I want to be vaild during a single request. I am not using Session, as this would make the value global for the entire navigation session.
So I have put thie value in a static field of a class. Great, but then I discovered that such fields are even more global, that is, they stay set for the entire application! This means that there could be random interaction among navigation sessions.
So the question is: is there a safe place I can put a global variable, which will be
global throughout the request
reset after the request is completed
not affected by any other request, either of the same user or by other users
Thanks
Palantir
EDIT
I'll elaborate. I have a piece of code in my master page, which I need to hide on certain conditions, of which I am aware in the controller only. I thought about setting a static variable in the controller, which then would be queried by the master page, but now I see there could be a better way...
Use HttpContext.Items - a per-request cache store. Check out this article on 4guysfromrolla for more details.
It should work fine in ASP.NET MVC. You may wish to derive your master page from a base class (either via code-behind or using the Inherits directive) and have a protected method on the base class that inspects HttpContext.Items and returns, e.g. true/false depending whether you want to display the conditional code.
TempData lasts until the next request as already noted.
But there are also two other dictionaries scoped to the single request.
{Controller,ViewPage}.ViewData
Context.Items
To communicate from controller to (master) page ViewData is probably the better choice.
Two approaches come to mind:
Create a base controller where you set this variable, and then have all your controllers inherit from that.
Use TempData - the problem here being that it sticks around for the next request. But maybe knowing that, you can work around it by using a GUID key to determine that you are, in fact, getting a new value when you need it.
I would probably go with 1).
The common way to access data in a MasterPage that is set in Controller (Action) is via ViewData["TheDataKey"] = "SomeValue".
This is relatively easy and there are a couple of ways that you can do it - depending on how your site works.
I'm interpreting your request as that you want a property or variable that exists for the duration of the request and is visible to the controller, model and master.
A static property is visible to the current application in ASP this means a load of users connecting at once, but not necessarily all of them. IIS will spawn new ASP applications as it needs to.
So the ways you can do this:
You can have a custom base class for your master page or a code-behind page (as all the WebForms stuff still works)
You can have a custom base class for your controllers.
You can get to one from the other, so:
void Page_Init( object sender, EventArgs e )
{
var ctrl = this.ViewContext.Controller as MyBaseController;
if ( ctrl != null )
{
MyLocalProp = ctrl.PropOnMyController;
}
}
This will then be available in the controller and the master page on a per Request basis.
Did you look into the tempData that is attached to the controller class? it is a simple dictionary that preserves it's value through one single request.That would meant that your data can only be accessed in the controller but that should not be a problem.
public class MyController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MyAction(string id)
{
this.TempData["Message"] = "YourData";
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(string Id)
{
var myData = this.TempData["Message"];
}
}
This works for me. I use it only to display warning messages and stuff like that.

How do you stub out User.Identity.Name in ASP.NET MVC?

I'm trying to unit test a page in my ASP.NET MVC application that, when posted to, will delete a user's item. I want to restrict this page so that it can only be posted to by the owner of said item. Originally, I wanted to just stick a quick check in the controller that checked if the HttpContext.Current.User.Identity.Name is equal to the owner of the item, but I quickly realized that unit testing this would be difficult.
Should I create an interface that provides a way to access the current logged-in user's name?
You can use this code
Controller CreateControllerAs(string userName) {
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
var controller = new SomeController();
controller.ControllerContext = mock.Object;
return controller;
}
It uses moq lib
The last line of your question is probably the quickest solution, however, I don't think you need to create the interface, HttpContext.Current.User is already an IPrincipal and HttpContext.Current.User.Identity is already an IIdentity.
If your checking logic uses either of those interfaces, the controller can be mocked to pass an IIdentity that you create instead of the HttpContext.Current.User.Identity
Hope that helps!
I use Thread.CurrentPrincipal.Identity.Name. It's trivial to create a GenericPrincipal for a unit test and attach it.

Resources