Using Request.Files.Count with TestControllerBuilder from MvcContrib? - asp.net-mvc

I have a controller action in ASP.NET MVC that handles uploaded files. However, it seems there is no way to call Request.Files.Count while using MvcContrib's TestControllerBuilder.
I know I can work around this by abstracting Request.Files. My questions are:
Is it indeed the case that there is no direct way to call Request.Files.Count when using the TestControllerBuilder? Or am I doing something wrong?
Is there a way to stub the call to Request.Files.Count while using TestControllerBuilder using Rhino Mocks?
Do you think I should submit a request or patch for handling Request.Files.Count to MvcContrib?
Example code:
I want to make sure that there is at least one file in the Request.Files collection so I have the following conditional in my action:
public class MyController : Controller {
public ActionResult Upload() {
if (Request.Files == null || Request.Files.Count == 0)
ViewData.ModelState.AddModelError("File", "Please upload a file");
// do stuff
return View();
}
}
I am using the TestControllerBuilder from MvcContrib to create the test double for my controller tests. However, the call to Request.Files.Count always seems to throw a an exception. For example running the following NUnit test throws a NotImplementedException during the call to controller.Upload() at the call to Request.Files.Count:
[Test]
public void Upload_should_return_default_view_given_one_file() {
MyController controller = new MyController();
TestControllerBuilder controllerBuilder = new TestControllerBuilder();
controllerBuilder.InitializeController(controller);
controllerBuilder.Files["file"] =
MockRepository.GenerateStub<HttpPostedFileBase>();
var result = controller.Upload() as ViewResult;
Assert.That(result.ViewData.ModelState.IsValid, Is.True);
result.AssertViewRendered().ForView(string.Empty);
}
I've also attempted stubbing the call to Request.Files.Count to no avail (I'm using Rhino Mocks). None of the below work (even if I change controller and/or controllerBuilder to a stub):
controllerBuilder.Stub(cb => cb.HttpContext.Request.Files.Count).Return(1);
controller.Stub(c => c.Request.Files.Count).Return(1);
Thanks

I submitted a patch on Github to MvcContrib mantainers, but the changes are very simple to make, the problem is that HttpFileCollectionBase is not implementing Count() and this[int index] methods so they must be overriden by the WriteableHttpFileCollection in MvcContrib.
I paste the code here for completeness, that must be added to WriteableHttpFileCollection class:
public override HttpPostedFileBase this[int index]
{
get { return files[AllKeys[index]]; }
}
public override int Count
{
get { return files.Count; }
}

I've used this approach to solve the problem.
SetUp the test:
string fileName = "Test 1.pdf";
FileStream stream = File.OpenRead("log4net.config");
MockRepository mock = new MockRepository();
builder.Files[fileName] = mock.CreateMock<HttpPostedFileBase>();
using (mock.Record())
{
Expect.Call(builder.Files[fileName].FileName)
.Return(fileName);
Expect.Call(builder.Files[fileName].ContentType)
.Return(contentType);
Expect.Call(builder.Files[fileName].ContentLength)
.Return(Convert.ToInt32(stream.Length.ToString()));
Expect.Call(builder.Files[fileName].InputStream)
.Return(stream);
}
using the mock:
foreach(string key in Request.Files.AllKeys)
{
int lenght = Request.Files[key].ContentLength;
if (lenght > 0)
{
Document document = new Document();
string fileName = Request.Files[key].FileName;
byte[] content= new byte[Convert.ToInt32(lenght)];
Request.Files[key].InputStream.Read(content, 0, content.Length);
document.SetContent(content);
document.MimeType = Request.Files[key].ContentType;
// do whatever you want...
}
}

Related

How to create a unit test that tests that a page requires authorization in mvc5

I am trying to work out how to write a unit test that will test that a controllers authorization is working. IE a user that is not logged in can't access the page. Does anybody know how to go about this? I am having trouble finding examples.
something like this (Pseudo Code)
[TestMethod]
public void Get_Auth_Page()
{
be_a_user_thats_not_logged_in = true;
// Arrange
MyController controller = new MyController();
// Act
var result = controller.Index();
// Assert
if(result.httpstatus == 403)
Assert.True();
}
If you are simply decorating your action method with [Authorize], you can just have a test that asserts the existence of the attribute:
[TestMethod]
public void Index_action_requires_authentication()
{
// If Index is overloaded, you might need to filter by argument list
MethodInfo indexMethod = typeof(MyController).GetMethod("Index");
bool requiresAuthentication =
Attribute.IsDefined(indexMethod, typeof(AuthorizeAttribute));
Assert.IsTrue(requiresAuthentication);
}
Obviously you aren't testing the Authorize implementation here, but it does serve to both document and to protect against developers accidentally removing it.
If you are running custom code, then you are probably returning a HttpStatusCodeResult, so you can just check for that:
public void Index_action_requires_authentication()
{
ActionResult result = new MyController().Index();
HttpStatusCodeResult statusCodeResult = result as HttpStatusCodeResult;
Assert.IsNotNull(statusCodeResult);
Assert.AreEqual(403, statusCodeResult.StatusCode);
}
If you are manually writing to the HttpResponse (Response.StatusCode or Response.Headers), then you'll need to mock the HttpContextBase as others have described.

How to mock test with asp.net mvc

I am writing an app that I have been deploying to appharbor. I am having trouble getting my project to build now because I have expanded my tests. I believe the issue is that I am using a db initializer to populate the database with test seed data. These tests pass on my local box but once I deploy the tests fail on appharbor. I suspect I need to mock data but I am not sure how to do this. As an example, here is a controller test that I have for one of my action methods.
Controller
// GET: /Lead/Url
// TODO: Add optional url parameters
public ActionResult Url(string pfirstname, string plastname, string phone, int leadsource)
{
var lead = new Lead();
//store
lead.parent_FirstName = pfirstname;
lead.parent_LastName = plastname;
lead.parent_Phone = phone;
lead.LeadSourceID = leadsource;
lead.AgentID = 1;
if (ModelState.IsValid)
{
leadRepository.InsertLead(lead);
leadRepository.Save();
ViewBag.Message = "Success";
}
return View(lead);
}
//
// POST: /Lead/URL
[HttpPost, ActionName("Url")]
public ActionResult Url(Lead lead)
{
return View();
}
Unit Test
[TestMethod]
public void LeadUrl()
{
//ARRANGE
ILeadRepository leadrepository = new LeadRepository(new LeadManagerContext());
Database.SetInitializer<LeadManagerContext>(new LeadManagerInitializer());
LeadController controller = new LeadController(leadrepository);
//ACT
ViewResult result = controller.Url("Brad", "woods","465-456-4965",1) as ViewResult;
var lead = (Lead)result.ViewData.Model;
//ASSERT
Assert.AreEqual("Success" ,result.ViewBag.Message);
/*check for valid data */
Assert.AreEqual("Brad", lead.parent_FirstName);
}
Could someone please explain what I need to do next in order to improve code like this and get it to run again on app harbor successfully?
Actually you haven't verified interactions between controller and it's dependencies (repository). And this is the most important part - controller should pass your Lead object to repository. And then call Save (consider also to Unit Of Work pattern).
Also you should test controller in isolation, only this way you could be sure, that failing controller's test is an issue of controller, not of LeadRepository or LeadManagerInitializer.
// Arrange
Lead expected = CreateBrad();
var repository = new Mock<ILeadRepository>();
LeadController controller = new LeadController(repository.Object);
// Act
ViewResult result = (ViewResult)controller.Url("Brad", "woods", "465-456", 1);
// Assert
Lead actual = (Lead)result.ViewData.Model;
// All fields should be equal, not only name
Assert.That(actual, Is.EqualTo(expected));
Assert.AreEqual("Success", result.ViewBag.Message);
// You need to be sure, that expected lead object passed to repository
repository.Verify(r => r.InsertLead(expected));
repository.Verify(r => r.Save());
BTW I'd moved expected Lead creation to separate method:
private Lead CreateBrad()
{
Lead lead = new Lead();
lead.parent_FirstName = "Brad";
lead.parent_LastName = "woods";
lead.parent_Phone = "465-456";
lead.LeadSourceID = 1;
lead.AgentID = 1;
return lead;
}
Also you should override Equals method for Lead instances comparison:
public class Lead
{
// your current code here
public override bool Equals(object obj)
{
Lead other = obj as Lead;
if (other == null)
return false;
return other.parent_FirstName == parent_FirstName &&
other.parent_LastName == parent_LastName &&
// compare other properties here
other.AgentID == AgentID;
}
// also override GetHashCode method
}
BTW Why you don't pass Lead object to your action method (via POST message)?
You have to stub your repository. The easiest way to do that is to use mocking framework (I prefer Moq), and stub each method.
Something like this (for Moq):
var repository = new Mock<ILeadReporisory>();
repository.Setup(r => r.InsertLead(It.IsAny<Lead>()));
//raise, rinse, repeat
LeadController controller = new LeadController(repository.Object);

Simple Question: Setup mock for ajax request in asp.net mvc

I am new in unit test and MVC development.
I have a question for using moq for unit testing in asp.net mvc. I have a controller which accepts an ajax action:
[HttpPost,Authorize]
public ActionResult GrabLink()
{
string username = HttpContext.User.Identity.Name;
string rssUrl = Request.Params["Grablink"].ToString();
...}
This action deals with the http request which I generate from the view:
var mockRequest = new Moq.Mock<HttpRequestBase>();
but I can not find a way to define the parameters I used in the class. Also, is there any way to use the value binding provider directly to pass the value to the controller if I would like to do an ajax post?
I am a newbie in handling web request. If you have some good tutorial for better understanding the Http request (as well as the Httpcontext and related classes in asp.net) please post here. Thank you very much!
This works very well for me:
var controller = new HomeController();
var context = new Mock<HttpContextBase>(MockBehavior.Strict);
var controllerContext = new Mock<ControllerContext>();
controllerContext.SetupGet(x => x.HttpContext.User.Identity.Name)
.Returns("TestUser");
controllerContext.SetupGet(x => x.HttpContext.User.Identity.IsAuthenticated)
.Returns(true);
controllerContext.SetupGet(x => x.HttpContext.Request.IsAuthenticated)
.Returns(true);
controller.ControllerContext = controllerContext.Object;
// As a bonus, instantiate the Url helper to allow creating links
controller.Url = new UrlHelper(
new RequestContext(context.Object, new RouteData()), new RouteCollection());
This will allow you to initialize any user you want as an authenticated user, and the last line will allow you to user the Url helper within the controller even though you're calling it from a unit test.
As Scott said HttpContext makes Controllers hard to test. Anyway he's got a pretty solution at here.
BTW why didn't you make rssUrl a parameter if it is assigning by POST or GET?
e.g.
//POST: /GrabLink?rssUrl=bla bla...
[HttpPost,Authorize]
public ActionResult GrabLink(IPrincipal user, string rssUrl) {
string userName = user.Name;
}
Ok, #cem covered your second question very well.
For your first, nerddinner, and If I'm not mistaken, when you create a new Internet Application with Unit test, in Visual Studio, have the following mock classes for HttpContext. Its at the bottom of this file.
You could use these (or create a new Internet App +Tests with VS) and copy all the fake classes for your tests. (I wrote a Moq example below)
It looks like this:
public class MockHttpContext : HttpContextBase {
private IPrincipal _user;
public override IPrincipal User {
get {
if (_user == null) {
_user = new MockPrincipal();
}
return _user;
}
set {
_user = value;
}
}
public override HttpResponseBase Response
{
get
{
return new MockHttpResponse();
}
}
}
public class MockHttpResponse : HttpResponseBase {
public override HttpCookieCollection Cookies
{
get
{
return new HttpCookieCollection();
}
}
}
Not tested, but to Use mock it would look like this:
var fakeReqBase = new Mock<HttpRequestBase>();
fakeReqBase.Setup(f => f.User).Returns(new GenericIdentity("FakeUser"));
//generic identity implements IIdentity
fakeUserRepo.Object;//this returns fake object of type HttpRequestBase
Checkout the Moq Quickstart. Its quite easy to get used to, and the fluent interface really helps.

How to unit test a custom actionresult

I'm trying to unit test a custom action result. I recently watched Jimmy Bogard's excellent MvcConf video ("put your controllers on a diet") http://www.viddler.com/explore/mvcconf/videos/1/ and have started to try and implement some custom action results. I've managed that without a problem, the ActionResult works fine at runtime but I'm having trouble trying to unit test them.
Unfortunately in the code download there are no unit tests for Jimmy's custom action methods... which make me wonder.
I realise that action methods just return instances of the ActionResult types and its the MVC framework that actually calls the ExecuteResult method, which of course is not available when running the unit test. So my unit test is now just creating an instance of my custom ActionResult and I then call ExecuteResult.
Unfortunatley in the ExecuteResult method of my custom ActionResult it is also calling the ExecuteResult method of a ViewResult that I passed it. At that point it blows up. How should I be mocking/stubbing these things to get my unit test working?
public class SendToAFriendActionResult : ActionResult
{
public const string INVALID_CAPTCHA = "You don't appear to have filled out the two words from the security image correctly to prove you're a human. Please try again.";
public const string INVALID_MODEL_STATE = "You don't appear to have filled out all the details correctly. Please try again.";
public const string CONTACT_FAIL = "Unfortunately we experiend a problem sending the link. Please try again later.";
public const string SEND_TO_A_FRIEND_FAIL_KEY = "ContactFail";
private RedirectResult _success;
private ViewResult _failure;
private readonly SendToAFriendModel _model;
private readonly bool _captchaValid;
private readonly MessageBuilderServiceBase _mbs;
public RedirectResult Success
{
get { return _success; }
set { _success = value; }
}
public ViewResult Failure
{
get { return _failure; }
set { _failure = value; }
}
public SendToAFriendActionResult(RedirectResult success, ViewResult failure, SendToAFriendModel model, bool captchaValid, MessageBuilderServiceBase mbs)
{
_success = success;
_failure = failure;
_model = model;
_captchaValid = captchaValid;
_mbs = mbs;
}
public override void ExecuteResult(ControllerContext context)
{
if (!_captchaValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_CAPTCHA;
// On reaching this point I receive the error
// Object reference not set to an instance of an object
// as the MVC framework calls FindView
Failure.ExecuteResult(context);
return;
}
if (!context.Controller.ViewData.ModelState.IsValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_MODEL_STATE;
Failure.ExecuteResult(context);
return;
}
_mbs.RecipientEmailAddress = _model.EmailRecipient;
_mbs.SendersName = _model.SendersName;
_mbs.Url = _model.URL;
var result = _mbs.sendMessage();
if (!result)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = CONTACT_FAIL;
Failure.ExecuteResult(context);
return;
}
Success.ExecuteResult(context);
}
}
Here's the start of my unit test ...
IMessageService _emailMessageSerivce;
IGalleryRepository _repository;
var stfModel = new SendToAFriendModel
{
SendersName = "Someone",
URL = "http://someurl.com",
EmailRecipient = "a-friend#somewherelse.com"
};
var failure = new ViewResult() {ViewName ="SendToFriend"};
const bool captchaValid = false;
var fakeControlllerContext = MockRepository.GenerateStub<ControllerContext>(null);
var stf = new SendToAFriendActionResult(null, failure, stfModel, captchaValid, null);
stf.ExecuteResult(fakeControlllerContext);
I've put comments in the SUT to show were the problem occurs.
I know I should be stubbing/mocking somehow but I just can't seem to resolve this.
From ASP.NET MVC 2 In Action (coauthored by Jimmy Bogard):
By taking that hard-to-test code out
of an action and putting it into the
Execute method of an action result,
you ensure that the actions become
significantly easier to unit-test.
That’s because when you unit-test an
action, you assert the type of action
result that the action returns and the
state of the action result. The
Execute method of the action result
isn’t executed as part of the unit
test.
Unit tests are designed to isolate behavior and concerns. You're mixing concerns by calling ExecuteResult from within your custom Action. Instead, I would have the SendToAFriendActionResult return the actual ActionResult (Failure or Success):
public ActionResult GetAction(..)
{
ActionResult result;
//logic here to determine which ActionResult to return
return result;
}
In your Controller:
public ViewResult SendToAFriend()
{
return SendToAFriendActionResult(null, failure, stfModel, captchaValid, null)
.GetAction();
}
This method will allow the MVC framework to do its job and isolates those concerns outside your custom ActionResult. Your test should assert that the correct type of Action, failure or success, is returned based on the parameters you set going in.

What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute?

I am trying to write a unit test for our log out method. Among other things it FormsAuthentication.SignOut(). However, it throws a System.NullReferenceException.
I've created a mock; HttpContext (using Moq), but it is obviously missing something.
My mock context contains:
A mocked HttpRequestBase on Request
A mocked HttpResponseBase on Response
With a HttpCookieCollection on Request.Cookies and another on Response.Cookies
A mocked IPrincipal on User
I am aware I could go the wrapper route and inject an empty FormsAuth wrapper object in it's place, but I would really like to avoid the 3 additional files just to fix one line of code. That and I am still curious for an answer
So my question is "What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute."
The NullReferenceException in this case is actually being thrown by the call:
current.Request.Browser["supportsEmptyStringInCookieValue"]
You can test this assertion by calling:
HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
...which will also return the NullReferenceException. Contrary to the accepted answer, if you attempt to call:
CookielessHelperClass.UseCookieless(current, false, CookieMode)
...from the immediate window, this will return without error.
You can fix the exception like this:
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
...and the FormsAuthentication.SignOut() call will now succeed.
You can always wrap FormsAuthentication.SignOut() into another method and stub / mock it.
Create IFormsAuthenticationWrap interface.
public interface IFormsAuthenticationWrap
{
void SignOut();
}
Create wrap class that implements IFormsAuthenticationWrap
public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
Your calling class is going to look something like this:
public class LogOutClass
{
private readonly IFormsAuthenticationWrap _formsAuthentication;
public LogOutClass() : this (new FormsAuthenticationWrap())
{
}
public LogOutClass(IFormsAuthenticationWrap formsAuthentication)
{
_formsAuthentication = formsAuthentication;
}
public void LogOutMethod()
{
// Code before SignOut
_formsAuthentication.SignOut();
// Code after SignOut
}
}
Now let's get to our test. You can stub / mock with Moq but I'm going to show here how you can do it manually.
Create your stub / mock class:
public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
public void SignOut()
{
}
}
And the last write the test:
[TestMethod]
public void TestLogOutMethod()
{
var logOutClass = new LogOutClass(new FormsAuthenticationStub());
logOutClass.LogOutMethod();
}
Here's the code for signout.
public static void SignOut()
{
Initialize();
HttpContext current = HttpContext.Current;
bool flag = current.CookielessHelper.DoesCookieValueExistInOriginal('F');
current.CookielessHelper.SetCookieValue('F', null);
if (!CookielessHelperClass.UseCookieless(current, false, CookieMode) || current.Request.Browser.Cookies)
{
string str = string.Empty;
if (current.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
{
str = "NoCookie";
}
HttpCookie cookie = new HttpCookie(FormsCookieName, str);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new DateTime(0x7cf, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
{
cookie.Domain = _CookieDomain;
}
current.Response.Cookies.RemoveCookie(FormsCookieName);
current.Response.Cookies.Add(cookie);
}
if (flag)
{
current.Response.Redirect(GetLoginPage(null), false);
}
}
Looks like you need a CookielessHelperClass instance. Too bad it's internal and sealed - there's no way to mock it unless you're using TypeMock. +1 for wrapper suggestions :)
The wrapper is the clean way to go.
You mentioned in a comment that "this is going to be quite a big application", that's another argument to use the wrapper not the opposite. In a big application you want to have clear dependencies, and you want tests to be done easily.
You are trading clean dependencies that can be easily injected over obscure dependencies to the internal workings of asp.net in your tests.
On a different note: Use Reflector. I honestly don't know the inner dependencies of this specific part of asp.net, but you can clear any doubts with reflector.
Don't mock HttpContext, use a real one in your tests. This way you don't have to mock all these Http* stuff. You can use Ivonna and test your method directly, without mocking all these dependencies and getting mysterious exceptions.

Resources