34

I have an action that relies on User.Identity.Name to get the username of the current user to get a list of his orders:

public ActionResult XLineas()
    {
        ViewData["Filtre"] = _options.Filtre;
        ViewData["NomesPendents"] = _options.NomesPendents;
        return View(_repository.ObteLiniesPedido(User.Identity.Name,_options.Filtre,_options.NomesPendents));
    }

Now I'm trying to write unit tests for this, but I get stuck on how to provide a Mock for User.Identity.Name. If I run my test as I have it (without mock for User...), I get a Null.. exception.

Which is the correct approach for this? I'm thinking that my Action code is not good for unit testing.

Carles Company
  • 7,118
  • 5
  • 49
  • 75

2 Answers2

67

You can use this code

public SomeController CreateControllerForUser(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 mocking framework, but sure you can use anything you like.

bicycle
  • 8,315
  • 9
  • 52
  • 72
Sly
  • 15,046
  • 12
  • 60
  • 89
  • For anyone else, like me, who was wondering: this code sample uses [Moq](https://code.google.com/p/moq/). – Joel Malone Mar 26 '13 at 09:51
  • 3
    In WebAPI i did like this ``MyController controller = new MyController(); controller.User = new GenericPrincipal(new GenericIdentity(username, "Passport"), new[] { "tester" });`` – Ravi Apr 22 '15 at 20:55
  • @Ravi How? [Controller.User is not settable](https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.user)(though this might have changed in the past 2.5 years) – Wolfzoon Aug 08 '17 at 15:34
  • 1
    @Wolfzoon I have to be honest. I am no more using DotNet. Did not remember answering this :-). Forgive me. – Ravi Aug 09 '17 at 00:05
21

A better way of doing this would be to pass a string argument userName (or an IPrincipal argument user, if you need more information than just the name) to the ActionMethod, which you "inject" in a normal request using an ActionFilterAttribute. When you test it, you just supply your own mock object, as the action filter's code will not run (in most cases - there are ways to, if you specifically want to...)

Kazi Manzur Rashid describes this in detail under point 7 in an excellent blog post.

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • Great. I have to look more into ActionFilters... Thanks. – Carles Company Sep 07 '09 at 15:20
  • I often mock IPrincipal for my tests. It allows me to test user informations (username) but also authorization (User.Identity.IsInRole). – mberube.Net Oct 08 '09 at 03:47
  • 1
    So do I. On the other hand, mocking IPrincipal just for getting access to the current user's username - and *nothing* else - is on the edge of overkill... :) – Tomas Aschan Oct 08 '09 at 08:20
  • When passing an IPrincipal or IIdentity object, you'll need to use an interface and wrapper. Just using the ActionFilter will result in an error saying you can't implement an interface. – Cavyn VonDeylen Jan 14 '13 at 20:21
  • The article by Kazi Manzur Rashid gives a great starting point. I prefer to modify the code to throw an InvalidOperationException immediately if the user is not authorized. Also note that you should probably write units tests for your ActionFilterAttribute anyway, and will still have to mock these things at least once. – jakejgordon Jul 07 '14 at 02:26
  • @rob: Thanks for making me aware. The post is quite old (it wasn't brand new when I wrote this answer over 7 years ago...) and I can't find it archived anywhere either. However, a lot has happened in the ASP.NET landscape since then, so I bet there are other articles describing newer solutions to similar problems. – Tomas Aschan Oct 28 '16 at 06:57
  • @rob: I did find the article in [the wayback machine](https://web.archive.org), though - link updated :D – Tomas Aschan Oct 28 '16 at 07:00