I have the following test method for an Asp.Net controller action method.
[TestMethod]
public void Get()
{
var controller = new MyController();
var result = controller.GetAListOfRecords();
Assert.IsNotNull(result);
Assert.IsTrue(result.Count() > 0);
}
And the action is
[Authorize(Roles = "Users")]
public IQueryable<MyModel> GetAListOfRecords()
{
var user = User.Identity.Name; // null when called from the test method
var q = from t in ........
select t;
return return string.IsNullOrEmpty(user) ? Enumerable.Empty<MyModel>().AsQueryable() : q;
}
However, the var user = User.Identity.Name will not get current user name in the test class (it will actually be assigned an empty string). Is it possible to set user in the test method?
Update:
I tried the following in the test class but .....Identity.Name is read-only.
controller.RequestContext.Principal.Identity.Name = #"mdynycmas\wangyi";
Actually, you're using the User property on your Controller object. This property is using the underlying HttpContext.Current. You need to mock the HttpContextBase and return a test user, see for instance How to mock Controller.User using moq.
You need to implement some mocks in your unit tests. Also I suggest you to remove your direct dependency of HttpContext, and better create a wrapper object instead, so you can mock it easily. See more information in this link about this approach.
Hope it helps!
Related
Problem
I want to unit test a method in my repository class that checks if a record should be updated or created new.
How do I test the main function without actually having the unit test attempt to insert or query the db?
Code
I have the following repository class:
public class WidgetRepository()
{
public bool InsertOrUpdateWidget(Widget widgetToEval)
{
var retval = false;
var existingRecord = FindExistingWidget(widgetToEval);
if (existingRecord == null)
{
retval = InsertNewWidget(widgetToEval);
}
else
{
retval = UpdateExistingWidget(widgetToEval, existingRecord);
}
return retval;
}
Unit Test
[Fact]
public void Insert_New_Widget()
{
var repo = GetEmptyRepository();
var newWidget = new Widget()
{
ID = 1,
Name= "test",
Description= "Test widget",
};
var result = repo.InsertOrUpdateWidget(newWidget);
Assert.True(result);
}
private IWidgetRepository GetEmptyRepository()
{
var repo = new Mock<IWidgetRepository >();
repo.Setup(s => s.FindExistingWidget(It.IsAny<Widget>())).Returns((Widget)null);
repo.Setup(s => s.InsertNewWidget(It.IsAny<Widget>())).Returns(true);
return repo.Object;
}
In the unit test, I'm trying to mock the FindExistingWidget method and have it return a null object. I've also mocked the insert function and have it return a true.
When I run this test, instead of returning a true, it returns false.
Edit 1
So I understand that I shouldn't mock the repo... I should just create an object of this type because I should only mock things my code needs / dependencies.
But I guess the question is then how do i prevent the code from actually attempting to connect to the db when it runs the FindExistingWidget() method or the actual InsertNewWidget method?
I just want to unit the test the logic inside the InsertorUpdate method to make sure its doing the right thing
When you want to test your repository you don't test the interface. You mock your repo when you want you want to test somehting using it. It's 'unit' test so you should test every method while it's sepereated from the others.
You should be testing WidgetRepository and not IWidgetRepository.
As the previous answers states, you are not Mocking the call to InsertOrUpdateWidget(), so its returning false (it's not even calling the code in the concrete class)
If you are going to mock your repository and you just want it to return true, then do this;
private IWidgetRepository GetEmptyRepository()
{
var repo = new Mock<IWidgetRepository >();
repo.Setup(s => s.InsertOrUpdateWidget(It.IsAny<Widget>())).Returns(true);
return repo.Object;
}
You can't mock just a portion of the WidgetRepository class. In the instance you are using above is your mock, and based on your setup above, you did not implement the function you are calling (InsertOrUpdateWidget) with repo.Setup. Since it returns a boolean, it will default to the value false. This function may be implemented in your concrete implementation of IWidgetRepository, but it isn't in your mock. The return statement return repo.Object; is not of WidgetRepository, but of a mocked version of IWidgetRepository. These are two different implementations, and only one of them implements InsertOrUpdateWidget. It isn't the one you are testing.
I'm building a Web API in ASP.NET Core, and I want to unit test the controllers.
I inject an interface for data access, that I can easily mock. But the controller has to check the headers in the Request for a token, and that Request doesn't seem to exist when I simply instantiate the controller myself, and it is also get-only, so I can't even manually set it. I found lots of examples to mock an ApiController, but that isn't .NET core. Also many tutorials and examples of how to unit test .net core controllers, but none actually used the HttpRequest.
I built an MCVE to demonstrate this:
[Produces("application/json")]
[Route("api/Players")]
public class PlayersController : Controller
{
private IAccessor accessor;
public PlayersController(IAccessor ac = null):base()
{
accessor = ac ?? AccessorFactory.GetAccessor();
}
/// <summary>
/// Get all players. Must be logged in.
/// </summary>
/// <returns>Ok or Unauthorized.</returns>
[HttpGet]
public IActionResult Get()
{
Player client = accessor.GetLoggedInPlayer(Request.Headers["token"]); // NRE here because Request is null
if (client == null) return Unauthorized();
return Ok(accessor.GetAllPlayers());
}
}
I'm using Moq and MSTest in my test project, and inject a mocked IAccessor. How do I inject the Request, or initialize it with the controller? I guess my last resort would be reflection, but I really want to avoid that.
When creating an instance of the controller under test, make sure to assign a HttpContext that contains the required dependencies for the test to be exercised to completion.
You could try mocking a HttpContext and providing that to the controller or just use DefaultHttpContext provided by the framework
//Arrange
var mockedAccessor = new Mock<IAccessor>();
//...setup mockedAccessor behavior
//...
var httpContext = new DefaultHttpContext(); // or mock a `HttpContext`
httpContext.Request.Headers["token"] = "fake_token_here"; //Set header
//Controller needs a controller context
var controllerContext = new ControllerContext() {
HttpContext = httpContext,
};
//assign context to controller
var controller = new PlayersController (mockedAccessor.Object){
ControllerContext = controllerContext,
};
//Act
var result = controller.Get();
//...
The above assumes you already know how to mock the controller dependencies like IAccessor and was meant to demonstrate how to provide framework specific dependencies needed for the test.
Here is the scenario:
I'm writing a test for my controller and need to setup a view model titled CheckoutViewModel. My controller method, Products does not take CheckoutViewModel as a parameter, so I cannot pass it in that way.
Currently, the test fails returning a Null Exception because CheckoutViewModel is not getting set and called.
Question: How can I setup my CheckoutViewModel with data.
Error Details:
System.NullReferenceException
Object reference not set to an instance of an object
Current Test
[TestMethod]
public void Products_ProductControllerIsCalled_ReturnsViewWithProducts()
{
// Arrange
var currentSession = _autoMoqer.GetMock<ICurrentSession>().Object;
ProductController productController = new ProductController(currentSession);
var checkoutViewModel = new CheckoutViewModel
{
CheckoutId = new Guid()
};
// Act
ActionResult result = productController.Products();
// Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
}
Controller
[AccectReadVerbs]
public ActionResult Products()
{
CheckoutViewModel checkoutViewModel = GetCheckoutViewModel();
var checkoutId = checkoutViewModel.CheckoutId;
var result = _productOrchestrator.Products(checkoutId, currentSession)
return View(result);
}
Failing on this method
private CheckoutViewModel GetCheckoutViewModel()
{
if(Session["CheckoutViewModel"] == null)
{
return new CheckoutViewModel();
}
return (CheckoutViewModel)Session["CheckoutViewModel"];
}
If GetCheckoutViewModel has some dependencies on i.e services, dbConnection or other complex classes, you need to add a class with an interface, move the method for GetCheckOutViewModel to the class and take the new interface as a dependency to the controller. Then you need to mock the new interface.
Or edit your viewmodel to take interface dependencies on the stuff that stands in the way of unit testing, i.e the Session.
I think you could create some interface:
public interface ISessionManager
{
Session session {get; set;}
}
Then your controller constructor:
public ProductsController(ISessionManager sm)
{
_sessionManager = sm;
}
Then you can pass a mocked instance to your controller.
I'm guessing that the exceptions is due to the fact that when you're running the unit test there will not be any (webserver) session available. What you want do is to isolate your tests from any external dependencies - and a session state that is part of the webserver hosting environment would be an external dependency.
To solve this you need to either mock or stub out the Session object from your test. There are many ways to do this, but the easiest way would be to make Session a public property on the Controller. From your test you would then set the Session to an instance you create within your test.
I have never run Unit testing before and I am just trying to run an example I have found on the net over and over in regards to the view name.
My Test code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Admin.Web.API.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Mvc;
namespace Admin.Web.API.Controllers.Tests
{
[TestClass()]
public class HomeControllerTests
{
[TestMethod()]
public void IndexTest()
{
HomeController controller = new HomeController();
var result = controller.Index() as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
}
}
The error I am getting is System.NullReferenceException: Object reference not set to an instance of an object. on the Line that sets the view result.
What do I need to do to get this working? Is there anything out there that is more descriptive as to Unit testing examples?
Edit one
Controller Code for Index
public ActionResult Index()
{
if (this.Session["UserID"] == null)
{
return View("Login");
}
else
{
ViewBag.Title = "Index";
ViewBag.SiteID = this.Session["SiteID"];
ViewBag.AssemblyVersion = this.Session["AssemblyVersion"];
ViewBag.UserFirstName = this.Session["FirstName"];
GoogleAnalytics _oGoogleAnalytics = new GoogleAnalytics();
ViewBag.GoogleAnalytics = _oGoogleAnalytics.GetGoogleAnalytics(
this.Session["GoogleAnalyticsAccountCode"].ToString(),
Convert.ToBoolean(this.Session["UseGoogleAnalytics"]));
return View("Index");
}
}
You're using the controllers Session property, which will be null because you haven't supplied the controller with the information it needs to create it. This information is normally supplied automatically when running under the Asp.Net pipeline. You can verify this by debugging and stepping into (F11) the Index action method and hovering over Session.
You need to set the ControllerContext property of the controller. Even better would be to use the Authorize attribute on your action method / controller. This is a good post about how to do Forms authentication in MVC.
The simplest way to get your test going though is to use the Controller's User property. You also do this by creating an instance of ControllerContext and setting its HttpContext property, probably by Moq'ing HttpContextBase so that you can return whatever IPrincipal you want.
So this is what you'd need to add after you new up your controller (I'm showing you with the Moq framework, but the VS UnitTest tools might provide its own way to do mocks. Either is fine):
var principalMock = new Mock<IPrincipal>(); // Mock<T> is from the Moq framework
principalMock.Setup(x => x.IsAuthenticated).Returns(true); // Or false, depending on what you're testing
var httpContextMock = new Mock<HttpContextBase>();
httpContextMock.Setup(x => x.User).Returns(principalMock.Object);
var controllerContext = new ControllerContext { HttpContext = contextMock.Object };
conrollerContext.Controller = controller;
controller.ControllerContext = controllerContext;
After you have all that setup, then you can safely call the action method you're testing.
controller.Index() is not returning a ViewResult and thus result is null.
result.ViewName is then failing as you are dereferencing null.
You need to look at the definition of Index and determine what type of object it is returning. Alternatively you can create a variable for controller.Index() and see what type it has.
EDIT:
Taking a deeper look at Index() it appears that you are relying on some data points that might not be available. The controller is relying on its framework (in this case your test framework) to provide certain pieces, IIRC the Session and ViewBag are two of those pieces.
This is probably going to turn out to be a case of just needing another pair of eyes. I must be missing something, but I cannot figure out why this kind of thing cannot be tested for. I'm basically trying to ensure that unauthenticated users cannot access the view by marking the controller with the [Authorize] attribute and I'm trying to tests this using the following code:
[Fact]
public void ShouldRedirectToLoginForUnauthenticatedUsers()
{
var mockControllerContext = new Mock<ControllerContext>()
{ DefaultValue = DefaultValue.Mock };
var controller = new MyAdminController()
{ControllerContext = mockControllerContext.Object};
mockControllerContext.Setup(c =>
c.HttpContext.Request.IsAuthenticated).Returns(false);
var result = controller.Index();
Assert.IsAssignableFrom<RedirectResult>(result);
}
The RedirectResult I'm looking for is some kind of indication that the user is being redirected to the login form, but instead a ViewResult is always returned and when debugging I can see that the Index() method is successfully hit even though the user is not authenticated.
Am I doing something wrong? Testing at the wrong level? Should I rather be testing at the route level for this kind of thing?
I know that the [Authorize] attribute is working, because when I spin up the page, the login screen is indeed forced upon me - but how do I verify this in a test?
The controller and index method are very simple just so that I can verify the behaviour. I've included them for completeness:
[Authorize]
public class MyAdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
Any help appreciated...
You are testing at the wrong level. The [Authorize] attribute ensures that the routing engine will never invoke that method for an unauthorized user - the RedirectResult will actually be coming from the route, not from your controller method.
Good news is - there's already test coverage for this (as part of the MVC framework source code), so I'd say you don't need to worry about it; just make sure your controller method does the right thing when it gets called, and trust the framework not to call it in the wrong circumstances.
EDIT: If you want to verify the presence of the attribute in your unit tests, you'll need to use reflection to inspect your controller methods as follows. This example will verify the presence of the Authorize attribute on the ChangePassword POST method in the 'New ASP.NET MVC 2 Project' demo that's installed with MVC2.
[TestFixture]
public class AccountControllerTests {
[Test]
public void Verify_ChangePassword_Method_Is_Decorated_With_Authorize_Attribute() {
var controller = new AccountController();
var type = controller.GetType();
var methodInfo = type.GetMethod("ChangePassword", new Type[] { typeof(ChangePasswordModel) });
var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
Assert.IsTrue(attributes.Any(), "No AuthorizeAttribute found on ChangePassword(ChangePasswordModel model) method");
}
}
Well you might be testing at the wrong level but its the test that makes sense. I mean, if I flag a method with the authorize(Roles="Superhero") attribute, I don't really need a test if I flagged it. What I (think I) want is to test that an unauthorized user doesn't have access and that an authorized user does.
For a unauthorized user a test like this:
// Arrange
var user = SetupUser(isAuthenticated, roles);
var controller = SetupController(user);
// Act
SomeHelper.Invoke(controller => controller.MyAction());
// Assert
Assert.AreEqual(401,
controller.ControllerContext.HttpContext.Response.StatusCode, "Status Code");
Well, it's not easy and it took me 10 hours, but here it is. I hope someone can benefit from it or convince me to go into another profession. :) (BTW - I'm using rhino mock)
[Test]
public void AuthenticatedNotIsUserRole_Should_RedirectToLogin()
{
// Arrange
var mocks = new MockRepository();
var controller = new FriendsController();
var httpContext = FakeHttpContext(mocks, true);
controller.ControllerContext = new ControllerContext
{
Controller = controller,
RequestContext = new RequestContext(httpContext, new RouteData())
};
httpContext.User.Expect(u => u.IsInRole("User")).Return(false);
mocks.ReplayAll();
// Act
var result =
controller.ActionInvoker.InvokeAction(controller.ControllerContext, "Index");
var statusCode = httpContext.Response.StatusCode;
// Assert
Assert.IsTrue(result, "Invoker Result");
Assert.AreEqual(401, statusCode, "Status Code");
mocks.VerifyAll();
}
Although, thats not very useful without this helper function:
public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated)
{
var context = mocks.StrictMock<HttpContextBase>();
var request = mocks.StrictMock<HttpRequestBase>();
var response = mocks.StrictMock<HttpResponseBase>();
var session = mocks.StrictMock<HttpSessionStateBase>();
var server = mocks.StrictMock<HttpServerUtilityBase>();
var cachePolicy = mocks.Stub<HttpCachePolicyBase>();
var user = mocks.StrictMock<IPrincipal>();
var identity = mocks.StrictMock<IIdentity>();
var itemDictionary = new Dictionary<object, object>();
identity.Expect(id => id.IsAuthenticated).Return(isAuthenticated);
user.Expect(u => u.Identity).Return(identity).Repeat.Any();
context.Expect(c => c.User).PropertyBehavior();
context.User = user;
context.Expect(ctx => ctx.Items).Return(itemDictionary).Repeat.Any();
context.Expect(ctx => ctx.Request).Return(request).Repeat.Any();
context.Expect(ctx => ctx.Response).Return(response).Repeat.Any();
context.Expect(ctx => ctx.Session).Return(session).Repeat.Any();
context.Expect(ctx => ctx.Server).Return(server).Repeat.Any();
response.Expect(r => r.Cache).Return(cachePolicy).Repeat.Any();
response.Expect(r => r.StatusCode).PropertyBehavior();
return context;
}
So that gets you confirmation that users not in a role don't have access. I tried writing a test to confirm the opposite, but after two more hours of digging through mvc plumbing I will leave it to manual testers. (I bailed when I got to the VirtualPathProviderViewEngine class. WTF? I don't want anything to do a VirtualPath or a Provider or ViewEngine much the union of the three!)
I am curious as to why this is so hard in an allegedly "testable" framework.
Why not just use reflection to look for the [Authorize] attribute on the controller class and / or the action method you are testing? Assuming the framework does make sure the Attribute is honored, this would be the easiest thing to do.
I don't agree with Dylan's answer, because 'user must be logged in' does not imply that 'controller method is annotated with AuthorizeAttribute'
to ensure 'user must be logged in' when you call the action method, the ASP.NET MVC framework does something like this (just hold on, it will get simpler eventually)
let $filters = All associated filter attributes which implement
IAuthorizationFilter
let $invoker = instance of type ControllerActionInvoker
let $ctrlCtx = instance or mock of type ControllerContext
let $actionDesc = instance or mock of type ActionDescriptor
let $authzCtx = $invoker.InvokeAuthorizationFilters($ctrlCtx, $filters, $actionDesc);
then controller action is authorized when $authzCtx.Result is not null
It is hard to implement this pseudo script in a working c# code. Likely, Xania.AspNet.Simulator makes it really simple to setup a test like this and performs exactly these step under the cover. here is an example.
first install the package from nuget (version 1.4.0-beta4 at the time of writing)
PM > install-package Xania.AspNet.Simulator -Pre
Then your test method could look like this (assuming NUnit and FluentAssertions are installed):
[Test]
public void AnonymousUserIsNotAuthorized()
{
// arrange
var action = new ProfileController().Action(c => c.Index());
// act
var result = action.GetAuthorizationResult();
// assert
result.Should().NotBeNull();
}
[Test]
public void LoggedInUserIsAuthorized()
{
// arrange
var action = new ProfileController().Action(c => c.Index())
// simulate authenticated user
.Authenticate("user1", new []{"role1"});
// act
var result = action.GetAuthorizationResult();
// assert
result.Should().BeNull();
}
For .NET Framework we use this class to verify that every MVC and API Controller have AuthorizeAttribute and that every API Controller should have a RoutePrefixAttribute.
[TestFixture]
public class TestControllerHasAuthorizeRole
{
private static IEnumerable<Type> GetChildTypes<T>()
{
var types = typeof(Startup).Assembly.GetTypes();
return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
}
[Test]
public void MvcControllersShouldHaveAuthrorizeAttribute()
{
var controllers = GetChildTypes<Controller>();
foreach (var controller in controllers)
{
var authorizeAttribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Mvc.AuthorizeAttribute), true) as System.Web.Mvc.AuthorizeAttribute;
Assert.IsNotNull(authorizeAttribute, $"MVC-controller {controller.FullName} does not implement AuthorizeAttribute");
}
}
[Test]
public void ApiControllersShouldHaveAuthorizeAttribute()
{
var controllers = GetChildTypes<ApiController>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.AuthorizeAttribute), true) as System.Web.Http.AuthorizeAttribute;
Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement AuthorizeAttribute");
}
}
[Test]
public void ApiControllersShouldHaveRoutePrefixAttribute()
{
var controllers = GetChildTypes<ApiController>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.RoutePrefixAttribute), true) as System.Web.Http.RoutePrefixAttribute;
Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement RoutePrefixAttribute");
Assert.IsTrue(attribute.Prefix.StartsWith("api/", StringComparison.OrdinalIgnoreCase), $"API-controller {controller.FullName} does not have a route prefix that starts with api/");
}
}
}
It is a bit easier in .NET Core and .NET 5<. Here a MVC Controller inherits from Controller that in turn inherits from ControllerBase. An Api Controller inherits directly from ControllerBase and therefore we can test MVC and API Controllers using a single method:
public class AuthorizeAttributeTest
{
private static IEnumerable<Type> GetChildTypes<T>()
{
var types = typeof(Startup).Assembly.GetTypes();
return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
}
[Fact]
public void ApiAndMVCControllersShouldHaveAuthorizeAttribute()
{
var controllers = GetChildTypes<ControllerBase>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(Microsoft.AspNetCore.Authorization.AuthorizeAttribute), true) as Microsoft.AspNetCore.Authorization.AuthorizeAttribute;
Assert.NotNull(attribute);
}
}
}