Unit test an Action with Recaptcha - asp.net-mvc

How I can test the following Action of the controller:
public ActionResult Edit(User usr)
{
if (!Microsoft.Web.Helpers.ReCaptcha.Validate(ConfigurationManager.AppSettings["reCaptchaPrivate"].ToString()))
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
if (ModelState.IsValid)
{code..... }}

I would start by abstracting the Captcha validation code:
public interface ICaptchaValidator
{
bool Validate();
}
and then have my controller look like this:
public class FooController: Controller
{
private readonly ICaptchaValidator _validator;
public FooController(ICaptchaValidator validator)
{
_validator = validator;
}
public ActionResult Edit(User usr)
{
if (!_validator.Validate())
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
...
}
}
Now you have weaken the coupling between your controller and the way those captchas are validated. That's a good thing as it makes your controller action far easier to unit test. We have successfully made our controller independent of the actual way validation is implemented.
Now just pick a mocking framework such as Rhino Mocks, Moq, NSubstitute and in your unit test inject a stubbed validator into this controller so that you can define behaviors on it.
Personally I would recommend you MvcContrib.TestHelper (which is based on Rhino Mocks) to test your ASP.NET MVC applications. It has many built-in goodies for mocking the HttpContext and make unit testing easy.
Here's an example of how the validation failure case could be tested:
[TestMethod]
public void FooController_Edit_Action_Should_Return_The_Wrong_Partial_If_Captcha_Validation_Fails()
{
// arrange
var validatorStub = MockRepository.GenerateStub<ICaptchaValidator>();
var sut = new HomeController(validatorStub);
var user = new User();
validatorStub.Stub(x => x.Validate()).Return(false);
// act
var actual = sut.Edit(user);
// assert
actual
.AssertPartialViewRendered()
.ForView("Wrong")
.WithViewData<User>()
.Equals(user);
Assert.IsFalse(sut.ModelState.IsValid);
}

As an alternative to Darin's answer, I've previously used a custom ActionFilter that processes the captcha and adds the error to the ModelState. It worked really well and meant that the captcha code wasn't part of the action method itself.

Related

Unit Testing a Controller - How Do I Handle the Connection String?

I can make it work, but I want to know what the best practice is and why. I have a Controller, a Model, and a Repository and now I want to Unit Test the Controller. I am just writing a simple test to ensure that the correct view is being returned.
This is my method in the controller:
public ActionResult Selections(SelectionsViewModel model)
{
for (int i = 0; i < model.Sends.Count; i++)
{
Send send = new Send(new SendService(new Database().GetConnectionString()))
{
SendID = model.Sends[i].SendID,
Title = model.Sends[i].Title,
Subject = model.Sends[i].Subject,
SentDate = model.Sends[i].SentDate,
TimesViewed = model.Sends[i].TimesViewed,
Include = model.Sends[i].Include,
Exclude = model.Sends[i].Exclude
};
send.UpdateIncludeExclude();
}
return View(model);
}
Here is my GetConnectionString() method in the Database class that is being sent via my SendService constructor.
public string GetConnectionString()
{
return System.Configuration.ConfigurationManager.ConnectionStrings["DEVConnectionString"].ToString();
}
And lastly, my unit test:
[Test]
public void TestAssignmentSelections()
{
var obj = new AssignmentController();
var actResult = obj.Selections() as ViewResult;
NUnit.Framework.Assert.That(actResult.ViewName, Is.EqualTo("Selections"));
}
Now, my unit test fails, and I get why. My unit test project has no access to the web.config of the project I am testing where my connection string resides.
I've done some research, and apparently just adding a web.config to my unit test project and putting the connection string in there as well will make it work.. but that seems like a hack.
What's the best way to go about this? Is there another way to write my code to accommodate for this?
You want to make your controller unit testable ? Don't do this.
new SendService(
With this code,you are hardcoding your concrete service implementation & your data access code implementation. In your unit test, you should not be really accessing the data from your database. Instead you should be providing a mock data access implementation.
Here comes interfaces, you need to create an interface for your SendService.
public interface ISendService
{
void SomeMethod();
}
now your SendService will be a concrete implementation of this interface
public class SendService : ISendService
{
public void SomeMethod()
{
// Do something
}
}
Now update your controller to have a constructor where we will inject an implementation of ISendService.
public class YourController : Controller
{
private ISendService sendService;
public YourController(ISendService sendService)
{
this.sendService = sendService;
}
public ActionResult YourActionMethod()
{
// use this.sendService.SomeMethod();
}
}
And you may use some dependency injection frameworks to tell the MVC framework which implementation of the interface to use when the code runs. If you are using MVC6,It has an inbuilt dependency injection provider you can use. So go to your Startup class and in your ConfigureServices method, you can map an interface to a concrete implementation.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISendService, SendService>();
}
}
If you are in a previous version of MVC, You may consider DI frameworks like Unity,Ninject etc. You can do the same approach for your Data access later / Service layers. ie: Create an interface for data access and inject that to your SendService.
public Interface IDataAccess
{
string GetName(int id);
}
and an implementation which uses your specific data access code/ORM
public class EFDataAccess : IDataAccess
{
public string GetName(int id)
{
// return a string from db using EF
}
}
So now your Service class will be
public class SendService : ISendService
{
private IDataAccess dataAccess;
public SendService(IDataAccess dataAccess)
{
this.dataAccess=dataAccess;
}
// to do : Implement methods of your ISendService interface.
// you may use this.dataAccess in those methods as needed.
}
In your unit tests, you can create a mock implementation of your interfaces which returns static data instead of accessing the database.
For example, If you are using Moq mocking framework, you can do this.
var m = new Mock<IDataAccess>();
var m.Setup(s=>s.GetName(It.IsAny<int>())).Returns("Test");
var s = new SendService(m);
var result= s.SomeMethod();

Test if save method is hit in unit test

I'm starting with unit testing in the asp.net mvc 4 framework.
I got a repository with basic crud methods and a save method. When I create a unit test I create a test repository and test if e.g. a item to the collection is added. That all goes smoothly but I cannot test if the save method is hit.
I tried to add a boolean property to the test repository which will be set to true if .save() is hit. But then I need to change the interface, and also the database repository. Which is in my opinion neither practical nor best practice.
What is the best method to test this? Thank you in advance for your answer.
My code:
the fake repository:
public class TestUserRepository : IUserManagementRepository
{
/// <summary>
/// entries used used for testing
/// </summary>
private List<User> _entities;
/// <summary>
/// constructor
/// </summary>
public TestUserRepository()
{
_entities = new List<User>();
_entities.Add(new User
{
Id = 1,
InsertDate = DateTime.Now,
LastUpdate = DateTime.Now,
Username = "TestUserName",
Password = "TestPassword"
});
}
...
public void Create(User task)
{
_entities.Add(task);
}
public void Save()
{
//do nothing
}
}
the controller to test:
[HttpPost]
public ActionResult Create(User user)
{
if (ModelState.IsValid)
{
_repository.Create(user);
_repository.Save();
return RedirectToAction("Index");
}
else
{
return View(user);
}
}
and the test
[TestMethod()]
public void CreateTest()
{
IUserManagementRepository repository = new TestUserRepository();
UserController controller = new UserController(repository);
User user = new User { Username = "UnitTestUserName", InsertDate = DateTime.Now, LastUpdate = DateTime.Now, Password = "Password" };
ActionResult actionResult = controller.Create(user);
User returnedUser = repository.FindBy(u => u.Username == "UnitTestUserName").First<User>();
Assert.IsNotNull(actionResult);
Assert.AreEqual(user, returnedUser);
}
You must be careful not to write a bunch of unit tests that just test your test repository.
Consider the following scenario:
You have a service method, that is supposed to add an item to your repository.
Your unit test calls this method, and you should verify that the appropriate "AddX" method was called on the repository.
This is a valid unit test scenario, to test it you can use your test repository. Since it is your test object, you have full control over it. You can expose properties such as "AddXMethodCallCount" or something similar.
Over time you will find yourself writing a lot of test code that is pretty much boilerplate. The alternative, which I strongly recommend, is to use a mocking framework:
https://stackoverflow.com/questions/37359/what-c-sharp-mocking-framework-to-use
It takes some getting used to, but once you get it, it will speed up your unit testing significantly.
If you don't want to use mocking yet, but want to still achieve your goal of verifying whether or not Save() is called, I would suggest just adding a publicly exposed SaveMethodCallCount property:
public class TestUserRepository : IUserManagementRepository
{
...
public SaveMethodCallCount {get; set;}
...
public void Save()
{
SaveMethodCallCount++;
}
}
This works, because in your unit test you can actually say:
TestUserRepository repository = new TestUserRepository();
The UserController does not care, as long as the passed in parameter implements the IUserManagementRepository interface. The controller interacts with the repository object through the interface, but the unit test does not have to, and the TestUserRepository, being a test class, is allowed to have much more functionality, that does not have to be exposed through the interface.
So your test could look something like:
[TestMethod()]
public void CreateTest()
{
TestUserRepository repository = new TestUserRepository();
UserController controller = new UserController(repository);
User user = new User { Username = "UnitTestUserName", InsertDate = DateTime.Now, LastUpdate = DateTime.Now, Password = "Password" };
ActionResult actionResult = controller.Create(user);
User returnedUser = repository.FindBy(u => u.Username == "UnitTestUserName").First<User>();
Assert.IsNotNull(actionResult);
Assert.AreEqual(user, returnedUser);
Assert.AreEqual(1, repository.SaveMethodCallCount);
}
To make my example complete, let me show you what this would look like if you used a mocking framework, like Moq. You can see some more examples here. The example test method uses Moq and Arrange/Act/Assert, and tests only one thing - that Save() is called when Create() is called.
[TestMethod()]
public void Test_SaveCalledWhenCreateCalled()
{
// Arrange
// First, instead of creating an instance of your test class, you create a mock repository.
// In fact, you don't need to write any code, the mocking framework handles it.
var mockRepository = new Mock<IUserManagementRepository>();
// and pass the mock repository (which implements the IUserManagementRepository) to your controller
UserController controller = new UserController(mockRepository);
// Act
ActionResult actionResult = controller.Create(user);
// Assert
// see how easy it is to do with a mocking framework:
mockRepository.Verify(rep => rep.Save(), Times.AtLeastOnce());
}

Mock a web service used in an action filter

I have an external-to-my-solution web service that I'm using in an ActionFilter. The action filter grabs some basic data for my MasterPage. I've gone back and forth between using an action filter and extending the base controller class, and decided the action filter was the best approach. Then I started unit testing (Yeah, yeah TDD. Anyway... :D )
So I can't mock (using Moq, btw) a web service in an action filter because I can't inject my mock WS into the action filter, since action filters don't take objects as params. Right? At least that's what I seem to have come to.
Any ideas? Better approaches? I'm just trying to return a warning to the user that if the web service is unavailable, their experience might be limited.
Thanks for any help!
namespace MyProject.ActionFilters
{
public class GetMasterPageData : ActionFilterAttribute
{
public ThatWS ws = new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
try {
DoStuff();
}
catch ( NullReferenceException e ) {
context.Session["message"] = "There is a problem with the web service. Some functionality will be limited.";
}
}
}
}
Here's a quick and dirty approach:
public class GetMasterPageData : ActionFilterAttribute
{
public Func<ISomeInterface> ServiceProvider = () => new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var result = ServiceProvider().SomeMethod();
...
}
}
And in your unit test you could instantiate the action filter and replace the ServiceProvider public field with some mocked object:
objectToTest.ServiceProvider = () => new SomeMockedObject();
Of course this approach is not as clean as the one suggested by #Ryan in the comments section but it could work in some situations.

asp.net mvc How to test controllers correctly

I'm having difficulty testing controllers. Original my controller for testing looked something like this:
SomethingController CreateSomethingController()
{
var somethingData = FakeSomethingData.CreateFakeData();
var fakeRepository = FakeRepository.Create();
var controller = new SomethingController(fakeRepository);
return controller;
}
This works fine for the majority of testing until I got the Request.IsAjaxRequest() part of code. So then I had to mock up the HttpContext and HttpRequestBase. So my code then changed to look like:
public class FakeHttpContext : HttpContextBase
{
bool _isAjaxRequest;
public FakeHttpContext( bool isAjaxRequest = false )
{
_isAjaxRequest = isAjaxRequest;
}
public override HttpRequestBase Request
{
get
{
string ajaxRequestHeader = "";
if ( _isAjaxRequest )
ajaxRequestHeader = "XMLHttpRequest";
var request = new Mock<HttpRequestBase>();
request.SetupGet( x => x.Headers ).Returns( new WebHeaderCollection
{
{"X-Requested-With", ajaxRequestHeader}
} );
request.SetupGet( x => x["X-Requested-With"] ).Returns( ajaxRequestHeader );
return request.Object;
}
}
private IPrincipal _user;
public override IPrincipal User
{
get
{
if ( _user == null )
{
_user = new FakePrincipal();
}
return _user;
}
set
{
_user = value;
}
}
}
SomethingController CreateSomethingController()
{
var somethingData = FakeSomethingData.CreateFakeData();
var fakeRepository = FakeRepository.Create();
var controller = new SomethingController(fakeRepository);
ControllerContext controllerContext = new ControllerContext( new FakeHttpContext( isAjaxRequest ), new RouteData(), controller );
controller.ControllerContext = controllerContext;
return controller;
}
Now its got to that stage in my controller where I call Url.Route and Url is null. So it looks like I need to start mocking up routes for my controller.
I seem to be spending more time googling on how to fake/mock objects and then debugging to make sure my fakes are correct than actual writing the test code. Is there an easier way in to test a controller? I've looked at the TestControllerBuilder from MvcContrib which helps with some of the issues but doesn't seem to do everything. Is there anything else available that will do the job and will let me concentrate on writing the tests rather than writing mocks?
Thanks
You can use some of the libraries that give you out of the box some of these objects. For example RhinoMock, NMock ... etc. I personally use Moq - it's good enough and free. What i like most in Moq is the linq expressions.
Most mocking engine will do all this for you. I use RhinoMocks but there are a lot more available. Also Moles is very new and interesting mocking engine (this generally comes with Pex which is yet more ammo in your unit testing arsenal)
MvcContrib + RhinoMocks. Check out the TestControllerBuilder in the MvcContrib.TestHelper library. Here's the official write-up: http://mvccontrib.codeplex.com/wikipage?title=TestHelper#Examples.
Here's an example of mocking a controller out for testing a UrlHelper: ASP.NET MVC: Mock controller.Url.Action
Here's a short explanation of how to use the TestControllerBuilder: http://codebetter.com/blogs/kyle.baley/archive/2008/03/19/testcontrollerbuilder-in-mvccontrib.aspx
Instead of mocking stuff, you can pass IAjaxRequest to constructor. Or make it base constructor class property (and use property injection). Or you can make your constructor implement IAjaxRequest and then apply global action filter on base constructor class that will setup IAjaxRequest.
This will help to abstract many things, including HttpContext stuff. Just don't abstract IHttpContext, abstract IUserContext, ISessionStorage, IAuthentication, IRequestDetails...
Another way is to use model binder directly on methods where you need specific information. See this post for example. You can make binder that will give you IsAjaxRequest, then you just make action to accept this parameter. Works very well because information is provided exactly to the method that needs it, not to the whole controller.

How can I unit test HandleUnknownAction() of an ASP.NET MVC Controller?

I have the following HandleUnknownAction set on my base controller class:
protected override void HandleUnknownAction(string action)
{
Response.Redirect("/");
}
How can I unit test that? Another point, is that way to handle the unknown action correct? Seems that calling RedirectToAction() would be more correct but the HandleUnknownAction doesn't have a return value.
The far I could get to test that is:
[Test]
public void TestHandleUnknownAction()
{
ctroler.ActionInvoker.InvokeAction(ctroler.ControllerContext, "unknown");
}
I'm stuck at it.
I don't think there's a need to test that HandleUnknownAction is invoked when a controller is missing an action. We trust the framework to handle that. So we can test the implementation by calling HandleUnknownAction directly with the mocking framework Moq. Should also be possible with Rhino Mocks.
public void TestHandleUnknownAction()
{
Mock<ControllerContext> cc = new Mock<ControllerContext>
(MockBehavior.Strict);
cc.Expect(c => c.HttpContext.Response.Redirect("/"));
TestHelperController controller = new TestHelperController();
controller.ControllerContext = cc.Object;
controller.InvokeUnknownAction("test");
}
Where TestHelperController makes the HandleUnknownAction accessible:
public class TestHelperController : RealController
{
public void InvokeUnknownAction(string action)
{
this.HandleUnknownAction(action);
}
}
That's fine for a simple Response.Redirect but that unit test code will not work if you want to do something more complicated like rendering an error view:
// TODO - Put some stuff into ViewData or a model
View("Error").ExecuteResult(Me.ControllerContext)

Resources