how to get the resulting HTML from a MVC action? - asp.net-mvc

Is it possible to get the resulting HTML that is outputting from an action via code?

Check out MvcIntegrationTestFramework

To be specific on why you would look at MvcIntegrationTestFramework.
It makes saves you writing your own helpers to stream result and is proven to work well enough. I'd assume this would be in a test project and as a bonus you would have the other testing capabilities once you've got this setup. Main bother would probably be sorting out the dependency chain.
private static readonly string mvcAppPath =
Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory
+ "\\..\\..\\..\\MyMvcApplication");
private readonly AppHost appHost = new AppHost(mvcAppPath);
[Test]
public void Root_Url_Renders_Index_View()
{
appHost.SimulateBrowsingSession(browsingSession => {
RequestResult result = browsingSession.ProcessRequest("");
Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
});
}

Here is an example of how I have done it in Razor syntax. I needed to get the html from one action as a string in another action (to the render as a pdf)
ViewResult viewResult = ActionYouWhatHtmlFrom(id);
using (var writer = new StringWriter())
{
ViewEngineResult result = ViewEngines
.Engines
.FindView(ControllerContext,
viewResult.ViewName, "_YourLayout");
var viewPath = ((RazorView)result.View).ViewPath;
var layoutPath = ((RazorView) result.View).LayoutPath;
var view = new RazorView(ControllerContext, viewPath, layoutPath, true, null);
var viewCxt = new ViewContext(
ControllerContext,
view,
viewResult.ViewData,
viewResult.TempData, writer);
viewCxt.View.Render(viewCxt, writer);

Related

Unit testing controllers using UserManager.FindById

I am new to unit testing .net applications and am having difficulty with what I would imagine to be a very simple case.
// GET: Entities
public ViewResult Index()
{
_User = UserManager.FindById(User.Identity.GetUserId());
return View(entityRepository.GetEntities(_User.entityId));
}
I want to test that the correct view is outputted but can't get past the user line. In other languages I would simply mock UserManager.FindById to always return some predefined object, but I can't get it to work.
Have been trying to follow the approach given here example mocking the IUserStore but can't get it to work with my example. To mock FindByNameAsync they have used store.As
Any advice would be gratefully received.
My attempt was to follow a similar method to that in link above. Obviously IUserPasswordStore is the wrong interface, but I am not sure how to find the correct one.
var store = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
store.As<IUserPasswordStore<ApplicationUser>>()
.Setup(x => x.FindById(It.IsAny<string>()))
.Returns(ApplicationUser)null);
EntitiesController controller = new EntitiesController();
var result = controller.Index() as ViewResult;
Assert.AreEqual("Index", result.ViewName);
So, thanks to the guidance from #Nkosi and #haim770 I think I have an answer. It still seems overly complex though, so would be interested to hear if you know how to simplify.
It started with needing to write more testable code, I installed Unity and started to inject dependencies allowing me to mock them out.
The solution that passes is:
Mock<IPrincipal> mockPrincipal;
string username = "test#test.com";
[TestInitialize]
public void TestInitialize()
{
//Arrange
var identity = new GenericIdentity(username, "");
var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, username);
identity.AddClaim(nameIdentifierClaim);
mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
}
[TestMethod]
public void EntitiesIndexDisplaysTheDefaultView()
{
var context = new Mock<HttpContextBase>();
var principal = mockPrincipal.Object;
context.Setup(x => x.User).Returns(principal);
var userManagerMock = new Mock<IUserStore<ApplicationUser>>(MockBehavior.Strict);
userManagerMock.As<IUserPasswordStore<ApplicationUser>>()
.Setup(x => x.FindByIdAsync(It.IsAny<string>()))
.ReturnsAsync(new ApplicationUser() { entityId = "id" });
var entitiesRepositoryMock = new Mock<IEntityRepository>();
entitiesRepositoryMock
.Setup(x => x.GetEntities(It.IsAny<string>()))
.Returns((IEnumerable<Entity>)new List<Entity>());
EntitiesController controller = new EntitiesController(new UserManager<ApplicationUser>(userManagerMock.Object),
entitiesRepositoryMock.Object);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("", result.ViewName);
}

MVC mocked HttpContext, model binding error

I am trying to follow the typical pattern of overriding the ControllerContext in order to mock HttpContext. In my case I specifically want to test HTTP POSTS so i need to mock Request.Form.
I have tried all 3 flavours found on google - with Moq, with Rhino.Mocks and with the MVCContrib.TestHelpers. For my specifics I've not been able to find a solution.
When my controller tries to bind the model, i get the following error:
Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.CollectionReplacer.GetUnvalidatedCollections(HttpContext context)
at System.Web.Helpers.Validation.Unvalidated(HttpRequest request)
at System.Web.Mvc.FormValueProviderFactory.<.ctor>b__0(ControllerContext cc)
at System.Web.Mvc.FormValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ValueProviderFactoryCollection.<>c__DisplayClassc.<GetValueProvider>b__7(ValueProviderFactory factory)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.Controller.TryUpdateModel(TModel model)
at eServices.Admin.Web.Controllers.User.UserController.Search() in UserController.cs: line 56
at eServices.Admin.Specs.Controllers.when_the_user_controller_is_posted_the_manage_users_find_form.<.ctor>b__1() in UserControllerSpecs.cs: line 96
Which appears to suggest it is not finding the mocked form. Here's the test code snippet:
MoqHttpContext MoqHttpContext = new MoqHttpContext();
var sut = new UserController(
UserRepository,
EmailService,
SessionProvider);
var controllerContext = new ControllerContext
(new RequestContext(MoqHttpContext.GetHttpContext(), new RouteData()), sut);
sut.ControllerContext = controllerContext;
MoqHttpContext.FormData.Add("FindCriteria.SearchText", "searchText");
MoqHttpContext.FormData.Add("FindCriteria.AccountIsPending", "true");
sut.Search();
...
in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Search()
{
var manageUsersViewModel = new ManageUsersViewModel();
TryUpdateModel(manageUsersViewModel);
...
Any ideas or better solutions for testing POSTs?
Using MvcContrib.TestHelper:
// arrange
var sut = new SomeController();
var tcb = new TestControllerBuilder();
tcb.InitializeController(sut);
var formValues = new FormCollection()
{
{ "FindCriteria.SearchText", "searchText" },
{ "FindCriteria.AccountIsPending", "true" },
};
sut.ValueProvider = formValues.ToValueProvider();
// act
var actual = sut.Search();
// assert
...
Any ideas or better solutions for testing POSTs?
Yes: instead of using TryUpdateModel have your controller actions directly take the view model as argument:
[HttpPost]
public ActionResult Search(ManageUsersViewModel model)
{
...
}
then in your unit test:
// arrange
var sut = new SomeController();
var model = new ManageUsersViewModel
{
FindCriteria = new FindCriteria
{
SearchText = "searchText",
AccountIsPending = true
}
};
// act
var actual = sut.Search(model);
// assert
...

ASP.NET MVC Moq unit testing against a controller that returns FileStreamResult

I am writing a test case against a controller that returns a pdf file.
Code for controller :
public FileStreamResult GeneratePdfReport(string context)
{
byte[] pdfReportContents = _helpPageBusinessService.GetHelpPagesAsPdf();
Stream stream = new MemoryStream(pdfReportContents);
HttpContext.Response.AddHeader("content-disposition", "attachment; filename=GSFA_Help_Pages_Printout.pdf");
return new FileStreamResult(stream, "application/pdf");
}
Unit test code :
[TestMethod]
public void GeneratePdf()
{
var controller = new HelpController(_helpPageBusinessServiceReportServices, Logger);
try
{
var result = controller.GeneratePdfReport("Work_Request_Section");
Assert.IsNotNull(result);
Assert.IsNotNull(result.FileStream);
}
finally
{
controller.Dispose();
}
}
This unit test case does not work, it always fail as HttpContext is null.
Does anybody out there know how to write unit test case against this type of controller ?
Much appreciated !
Jeffery
You need to mock the HttpContext and the response objects. Also your controller action could be shortened a bit:
public ActionResult GeneratePdfReport(string context)
{
byte[] pdfReportContents = _helpPageBusinessService.GetHelpPagesAsPdf();
HttpContext.Response.AddHeader("content-disposition", "attachment; filename=GSFA_Help_Pages_Printout.pdf");
return File(pdfReportContents, "application/pdf");
}
Following Darin Dimitrov's recommendation, I have came up with the following code. :)
[TestMethod]
public void GeneratePdf()
{
var controller = new HelpController(_helpPageBusinessServiceReportServices, Logger);
var httpContextBase = new Mock<HttpContextBase>
{
DefaultValue = DefaultValue.Mock
};
var responseObject = Mock.Get(httpContextBase.Object.Response);
responseObject.Setup(
s => s.AddHeader("content-disposition", "attachment; filename=GSFA_Help_Pages_Printout.pdf"));
controller.ControllerContext = new ControllerContext(httpContextBase.Object, new RouteData(), controller);
try
{
var result = controller.GeneratePdfReport("Work_Request_Section");
Assert.IsNotNull(result);
Assert.IsNotNull(result.FileStream);
Assert.IsTrue(result.FileStream.Length == 2000);
}
finally
{
controller.Dispose();
}
}
This is a classic testing issue. It stems from the fact this is more of an integration test rather than a unit test (touching the file system).
Mocking HTTP context has been a major issue back through the ASP.Net Web Forms era.Perhaps your test should focus on the HelpPageBusinessService.
If all else fails perhaps pass a helper class via dependency injection to your controller, that adds the header or mock with a mocking framework.
Using MOQ, you can even test if the header has been actually added to the response object
var httpContextBase = new Mock<HttpContextBase>();
_httpResponse = new Mock<HttpResponseBase>();
httpContextBase.Setup(c => c.Response).Returns(_httpResponse.Object);
controller = new Controller(businessService.Object)
{
ControllerContext = new ControllerContext { HttpContext = httpContextBase.Object }
};
Then you can verify
_httpResponse.Verify(r => r.AddHeader("content-disposition", "attachment; filename=GSFA_Help_Pages_Printout.pdf"));

Render ViewUserControl from Class Library

I want to render some HTML to use in an e-mail. I have a collection of objects that represent rows and columns, so I could be rendering the HTML in pure C# code using a StringBuilder.
But I prefer to have a ViewUserControl or just a ViewPage in a class library, that does the rendering. I want to be able to use the ViewUserControl and the rendering methostrong textd from outside an ASP.NET MVC app, e.g. a Windows Service.
How can I render a View from a class library?
I tried this, but the call to RenderPartial throws a NullReferenceException.
[TestMethod]
public void RenderViewToString()
{
string viewName = "EmailTest";
string viewData = "martin";
//Create memory writer
var sb = new StringBuilder();
var memWriter = new StringWriter(sb);
//Create fake http context to render the view
var fakeResponse = new HttpResponse(memWriter);
var fakeContext = new HttpContext(new HttpRequest("fake.html", "http://localhost/fake.html", ""), fakeResponse);
var fakeRouteData = new RouteData();
fakeRouteData.Values.Add("Controller", "Fake");
var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeContext), fakeRouteData, new FakeController());
HttpContext.Current = fakeContext;
//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(new ViewContext(fakeControllerContext, new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), memWriter), new ViewPage());
html.RenderPartial(viewName, viewData);
//Flush memory and return output
memWriter.Flush();
var htmlString = sb.ToString();
}
Try this:
public static string RenderPartialToString(string controlName, object viewData)
{
ViewDataDictionary vd = new ViewDataDictionary(viewData);
ViewPage vp = new ViewPage { ViewData = vd };
Control control = vp.LoadControl(controlName);
vp.Controls.Add(control);
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
return sb.ToString();
}
The issue you are getting is with the asp.net engine, which is what the default view engine uses.
You can avoid it by using a different view engine, like spark.
Another option is to use similar to your code, but starting an asp.net appdomain / I'll share some code that does so in a couple of days.

Using Moq to test methods that accept non-primative arguments

I'm trying to write a test for an ASP.Net MVC controller action.
I'd like to test that the action invokes a particular method on an injected service, so I'm mocking the service and using .Verify.
So in the simple case, I have the following action in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title)
{
_cmsService.AddPage(title);
return View("Edit");
}
using the service interface...
public interface ICmsService
{
void AddPage(string request);
}
and the following test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
cmsService.Setup(service => service.AddPage(pageTitle));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(pageTitle), Times.Once());
Now I want to refactor my service operation to use request and response objects...
public interface ICmsService
{
CmsServiceAddPageResponse AddPage(CmsServiceAddPageRequest request);
}
So I change my action accordingly...
public ActionResult Create(string title)
{
var request = new CmsServiceAddPageRequest()
{
PageName = title
};
var response = _cmsService.AddPage(request);
return View("Edit");
}
and also my test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
var request = new CmsServiceAddPageRequest() {PageName = pageTitle};
cmsService.Setup(service => service.AddPage(request));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(request), Times.Once());
But now when I run the test, I get the following message...
TestCase 'Web.Test.PageControllerTest.CreateNewPagePost'
failed: Moq.MockException :
Invocation was performed more than once on the mock: service => service.AddPage(value(Web.Test.PageControllerTest+<>c__DisplayClass1).request)
at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify[T,TResult](Mock mock, Expression`1 expression, Times times, String failMessage)
at Moq.Mock`1.Verify[TResult](Expression`1 expression, Times times)
PageControllerTest.cs(67,0): at Web.Test.PageControllerTest.CreateNewPagePost()
What should I be doing to test a method that accepts a non-primitive type?
Thanks
Sandy
I think a better alternative to the first answer would be to implement a custom matcher rather than change code to match your testing framework. From:http://code.google.com/p/moq/wiki/QuickStart
// custom matchers
mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge()
{
return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}
If you override Equals in the CmsServiceAddPageRequest object it should compare them correctly.

Resources