I've run into a bit of an issue trying to unit test an MVC site I have: I require a lot of the ASP.NET environment to be running (generation of httpcontexts, sessions, cookies, memberships, etc.) to fully test everything.
Even to test some of the less front end stuff needs memberships in order to properly work, and it's been finicky to get this all spoofed by hand.
Is there a way to spin up an application pool inside of NUnit tests? That seems like the easiest way.
If written properly, you shouldn't need to have a real context, real session, cookies, etc. The MVC framework by default provides a HttpContext that can be mocked/stubbed. I'd recommend using a mocking framework like Moq or Rhino Mocks and creating a MockHttpContext class that creates a mock context with all of the properties that you need to test against set up. Here's a mock HttpContext that uses Moq
/// <summary>
/// Mocks an entire HttpContext for use in unit tests
/// </summary>
public class MockHttpContextBase
{
/// <summary>
/// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
/// </summary>
public MockHttpContextBase() : this(new Mock<Controller>().Object, "~/")
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
/// </summary>
/// <param name="controller">The controller.</param>
public MockHttpContextBase(Controller controller) : this(controller, "~/")
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
/// </summary>
/// <param name="url">The URL.</param>
public MockHttpContextBase(string url) : this(new Mock<Controller>().Object, url)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MockHttpContextBase"/> class.
/// </summary>
/// <param name="controller">The controller.</param>
/// <param name="url">The URL.</param>
public MockHttpContextBase(ControllerBase controller, string url)
{
HttpContext = new Mock<HttpContextBase>();
Request = new Mock<HttpRequestBase>();
Response = new Mock<HttpResponseBase>();
Output = new StringBuilder();
HttpContext.Setup(x => x.Request).Returns(Request.Object);
HttpContext.Setup(x => x.Response).Returns(Response.Object);
HttpContext.Setup(x => x.Session).Returns(new FakeSessionState());
Request.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
Request.Setup(x => x.QueryString).Returns(new NameValueCollection());
Request.Setup(x => x.Form).Returns(new NameValueCollection());
Request.Setup(x => x.ApplicationPath).Returns("~/");
Request.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
Request.Setup(x => x.PathInfo).Returns(string.Empty);
Response.Setup(x => x.Cookies).Returns(new HttpCookieCollection());
Response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string path) => path);
Response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(s => Output.Append(s));
var requestContext = new RequestContext(HttpContext.Object, new RouteData());
controller.ControllerContext = new ControllerContext(requestContext, controller);
}
/// <summary>
/// Gets the HTTP context.
/// </summary>
/// <value>The HTTP context.</value>
public Mock<HttpContextBase> HttpContext { get; private set; }
/// <summary>
/// Gets the request.
/// </summary>
/// <value>The request.</value>
public Mock<HttpRequestBase> Request { get; private set; }
/// <summary>
/// Gets the response.
/// </summary>
/// <value>The response.</value>
public Mock<HttpResponseBase> Response { get; private set; }
/// <summary>
/// Gets the output.
/// </summary>
/// <value>The output.</value>
public StringBuilder Output { get; private set; }
}
/// <summary>
/// Provides Fake Session for use in unit tests
/// </summary>
public class FakeSessionState : HttpSessionStateBase
{
/// <summary>
/// backing field for the items in session
/// </summary>
private readonly Dictionary<string, object> _items = new Dictionary<string, object>();
/// <summary>
/// Gets or sets the <see cref="System.Object"/> with the specified name.
/// </summary>
/// <param name="name">the key</param>
/// <returns>the value in session</returns>
public override object this[string name]
{
get
{
return _items.ContainsKey(name) ? _items[name] : null;
}
set
{
_items[name] = value;
}
}
}
There's a few things that you could add further like a HTTP Headers collection, but hopefully it demonstrates what you can do.
To use
var controllerToTest = new HomeController();
var context = new MockHttpContextBase(controllerToTest);
// do stuff that you want to test e.g. something goes into session
Assert.IsTrue(context.HttpContext.Session.Count > 0);
With regards to Membership providers or other providers, you've hit on something that can be hard to test. I would abstract the usage of the provider behind an interface such that you can provide a fake for the interface when testing a component that relies on it. You'll still have trouble unit testing the concrete implementation of the interface that uses the provider however but your mileage may vary as to how far you want/have to go with regards to unit testing and code coverage.
I'm not aware of a way to do that since your code isn't in that process and requires a host that isn't in aspnet either. (I've been wrong before though haha)
Theres an older HttpSimulator from Phil Haack, have you given that a whirl?
http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx
You need to build wrapper interfaces for those services. The original MVC2 and MV3 starter project templates did this by default, but for some reason they dropped that in the latest versions.
You can try to find samples of the original AccountController code to give you a starting place. They used IMembershipService and IFormsAuthenticationService
It's relatively straightforward to mock session, context, etc..
Take a look at the MVCContrib project (http://mvccontrib.codeplex.com/) as they have a helper for creating controllers that have all the various contextual objects populated (like HttpContext).
Related
I've tried Dependency injection, but that always gives me a HttpContextAccessor.Current or ActionContext as null because I'm not in a request state (I think). So how can I get this context to just take a view, transform it to a html string (with Model if necessary) and throw it back in JS ? I even tried to call directly the Controller action, but it always gives HttpContext as null... I'm using Asp.NET Core 3.
Please, if someone has been going through, help me :-)
Thanks,
Edit:
I have an asp.net core based on Electron.net for a desktop application. I use a lot of IPC communication to retrieve data from backend c# using Electron.IpcMain.On. I register an action as listener in c# in a class. The main problem is that this class is really outside a normal HttpRequest or a Controller. Here is some sample code:
IpcBase Class
public abstract class IpcBase: IBaseIpcCommunicationClass
{
/// <summary>
/// Find a way to get rid of this and take more than the first window
/// </summary>
protected static BrowserWindow _mainWindow = Electron.WindowManager.BrowserWindows.First();
/// <summary>
/// Send an ipcEvent with a parameter class
/// </summary>
/// <param name="parameters">Parameters to fill</param>
public void RegisterIpcEvent<T>(IpcRegisterModel<T> registerModel) => Electron.IpcMain.On(registerModel.key, o => registerModel.action((T)
((JObject)o).ToObject(typeof(T))));
/// <summary>
/// Send a reply inside a registerevent
/// </summary>
/// <typeparam name="T">Type of model</typeparam>
/// <param name="model">model</param>
public void SendReply<T>(IpcSendParamsModel<T> model)
{
if (!string.IsNullOrEmpty(model.replyTo))
{
Electron.IpcMain.Send(_mainWindow, model.replyTo, model);
}
}
...
}
IpcUI (to get the controller view, just like an ajax call on a controller that retrieve the view in String (I already have that, but not with Ipc)
public class IpcUI: IpcBase
{
public IpcUI(IRazorViewToStringService razorViewRenderService)
{
Console.WriteLine("IpcUI::Constructor");
RegisterIpcEvent(new IpcRegisterModel<IpcSendParamsModel<AjaxPartialModel>>("renderPartial", async (param) =>
{
var param = new IpcSendParamsModel<AjaxPartialModel>("RenderPartial")
{
key = "renderPartial",
model = new AjaxPartialModel()
{
DataModel = "{items: [{\r\n MaterialIcon: \"\",\r\n Title: \"Games\",\r\n Selectable: true,\r\n Active: true,\r\n Key: \"GAMES\",\r\n BadgeCaption: \"new\",\r\n BadgeValue: \"123\",\r\n BadgeColor: \"red darken-1\",\r\n BadgePartialLink: \"\",\r\n BadgeContainerLink: \"\",\r\n BadgeModelLink: \"\",\r\n PartialLink: \"Home/Index\",\r\n ContainerLink: \"#body-content\",\r\n ModelLink: \"\"\r\n }] }".JsonDeserialize<MenuModelHeader>(),
PartialName = "PartialViews/_TopMenu"
}
};
try
{
param.results =
await razorViewRenderService.CreateAndResolveInstanceFromGeneric().RenderViewToStringAsync($"~/Views/{param.model.PartialName}.cshtml",
param.model.DataModel);
}
catch (Exception e)
{
IpcClasses.ExceptionManager.SendException(this, e, $"IpcUI params: {param.model.JsonSerialize()}");
}
}));
}
}
Razor Service (Mostly taken from here Generate string from view)
Added in startup:
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IRazorViewToStringService, RazorRazorViewToStringService>();
When I create an instance of IpcUI, DI gives me the service, but without any HttpContext or ActionContext... Sorry for the lack of information from my last edit :-). Hope it is a bit more specific.
Oh ! I forgot something, IpcUI is created at runtime not with a new (because that don't work) but with a custom extension function that retrieves the IServiceProvider for DI:
In startup
ExtensionsUtils.ServiceProvider = app.ApplicationServices;
In ExtensionsUtils
/// <summary>
/// This is called in configure services to get the collection of services in an extension static class
/// </summary>
public static IServiceProvider ServiceProvider { get; set; }
/// <summary>
/// Create a reference from type T with DI
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="o"></param>
/// <returns></returns>
public static T CreateAndResolveInstanceFromGeneric<T>(this T o)
{
return (T)ActivatorUtilities.CreateInstance<T>(ServiceProvider);
}
Edit 2:
I have tried to access IRazorViewToStringService from a real controller constructor and it's null again... What Am I doing wrong ???
private readonly IRazorViewToStringService _razorViewRenderService;
public BaseController(IRazorViewToStringService razorViewRenderService)
{
_razorViewRenderService = razorViewRenderService;
}
...
/// <summary>
/// Return a success http code and a View rendered as string
/// </summary>
/// <param name="ViewName">Name of MVC View (or PartialView)</param>
/// <param name="Model">Model to pass if any</param>
/// <returns>JSON: { result: "type", description: "Html Code" }</returns>
public async Task<ActionResult> CheckAndReturnView(string ViewName, object Model = null)
{
return Ok(await _razorViewRenderService.RenderViewToStringAsync(ViewName, Model));
}
In my Web api project I am having a controller DocumentViewerV1Controller which has a code as:
/// <summary>
///
/// </summary>
/// <param name="documentViewerService"></param>
public DocumentViewerV1Controller(IDocumentViewerService<HtmlViewInformation> documentViewerService)
{
_documentViewerHtmlService = documentViewerService;
}
/// <summary>
///
/// </summary>
/// <param name="documentViewerService"></param>
public DocumentViewerV1Controller(IDocumentViewerService<PageImage> documentViewerService)
{
_documentViewerImageService = documentViewerService;
}/// <summary>
/// Rendering Document as Html
/// </summary>
/// <returns></returns>
[HttpGet]
[Route(WebApiConfig.RootApiUri + "/v1/viewashtml/{docid}")]
public string ViewAsHtml(string docId)
{
var documentInfo = new DocumentInfo { DocumentName = HostingEnvironment.MapPath("~/Uploads/") + docId };
var response = _documentViewerHtmlService.RenderDocument(documentInfo, DocumentRenderType.Html);
return GenerateResponse(response);
}
When I run my service, make a call and debug the constructor initialization, It doesn't goes through the initialization which makes _documentViewerHtmlService as null, eventually fails returning Null reference Exception.
Is it possible to have service Interface as IDocumentViewerService?
Yes, but you'll need to remove one of the constructors or tell it which one to use.
container.RegisterType<IViewerInformation, HtmlViewerInformation>();
container.RegisterType<IDocumentViewerService<IViewerInformation>, DocumentViewerServce<IViewerInformation>>();
You may / may not also need to initialize some of the constructors which you can do by
container.RegisterType<IDocumentViewerService<IViewerInformation>, DocumentViewerServce<IViewerInformation>>(
new InjectionConstructor(...));
Assuming that the REST and Data access interactions will be somewhat standard, I want to be able to create an abstract type to represent entity objects to pass back and forth from REST client (e.g. JavaScript in a browser) to the persistence layer without writing/registering/routing a bunch of different WebAPI controllers.
How can one create one controller base type implementation to manage persistence of several different entity type REST interactions? I.e. how can I make this work with WebAPI?
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web;
using System.Web.Http;
namespace DynamicEntityApiControllers
{
/// <summary>
///
/// </summary>
public class EntityController<T> : ApiController
where T : EntityObject
{
private readonly IDaoProvider daoProvider;
/// <summary>
/// Initializes a new instance of the <see cref="EntityController{T}"/> class.
/// </summary>
/// <param name="daoProvider">The DAO provider.</param>
protected EntityController(IDaoProvider daoProvider)
{
this.daoProvider = daoProvider;
}
/// <summary>
/// Gets the name of the collection.
/// </summary>
/// <value>
/// The name of the collection.
/// </value>
protected string CollectionName
{
get
{
return (string)this.RequestContext.RouteData.Values["controller"];
}
}
/// <summary>
/// Gets the specified entity by key from the collection.
/// </summary>
/// <param name="key">The key.</param>
/// <returns></returns>
public T Get(string key)
{
return this.daoProvider.Output<T>(this.CollectionName, key).Data;
}
/// <summary>
/// Creates or updates the specified entity by key in the collection.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="updating">The updating.</param>
/// <returns></returns>
public T Put(string key, [FromBody] T updating)
{
var daoObject = DaoEntity.Create(this.CollectionName, key, updating);
return this.daoProvider.Input<T>(daoObject).Data;
}
/// <summary>
/// Creates or Updates the collection specified entirely.
/// </summary>
/// <param name="replacingCollection">The replacing collection.</param>
/// <returns></returns>
public IEnumerable<T> Put([FromBody] IEnumerable<T> replacingCollection)
{
return this.daoProvider.Input<T>(replacingCollection
.Select(o => DaoEntity.Create(this.CollectionName, o)));
}
/// <summary>
/// creates a new entity in the specified collection.
/// </summary>
/// <param name="creating">The creating.</param>
/// <returns></returns>
public T Post([FromBody] T creating)
{
var daoEntity = DaoEntity.Create(this.CollectionName, creating);
return this.daoProvider.Input(daoEntity).Data;
}
/// <summary>
/// Deletes the specified entity by key from the collection.
/// </summary>
/// <param name="key">The key.</param>
public void Delete(string key)
{
this.daoProvider.Delete(this.CollectionName, key);
}
}
}
the JavaScript client must look/act something like this:
$.ajax({url: '/Book', method: 'POST', data: {title: 'This StackOverflow Post', pageCount: '124' })
.done(function(data) {
$.get('/Book/'+data.Key).done(function(data) {
// this will return the book you just created
console.dir(data);
})
})
$.ajax({url: '/Book', method: 'POST', data: {title: 'Some other StackOverflow Post', pageCount: '236' })
.done(function(data) {
$.get('/Magazine/'+data.Key).done(function(data) {
// this will return 404 status code because you're asking for the book's key
// in the Magazines collection
console.dir(data);
})
})
the sample solution has a bunch TODO here (like actually do right with ETag header, if-match, etc...) but I had to do about 4 queries across StackOverflow to find these bits.
Prerequisites:
Autofac (IoC) integration with WebAPI
WebAPI Controller Location / DependencyResolver (these are not the same things)
C# Generics
Reflection IL Emitting
very minimal REST understanding and what an ETag is.
GIT source folder
briefly, here is the logic:
create a base type (entityobject)
identify all those entities at IoC Registration time via reflection (Implementer is responsible for specifying which assemblies to scan)
create concrete implementations of abstract class EntityController via IL Emitting
Tell the SupportsDynamicControllerTypeResolver about these dynamic types
IDaoProvider implementation is the "persistence" layer. implement your persistence logic here.. in this sample, an in memory dictionary is used.
And what happens when a request is made:
- RouteTable is interrogated (finds {controller}/{key} route which is an HttpRoute
- Controller name matches EntityObject implementer name minus Entity. I.e. BookEntity => BookController
- SupportsDynamicControllerTypeResolver is required here to get dynamic assembly types as valid candidates for WebApi to try to activate.
- HTTP Put/Get/Post/Delete methods find the correct method
- persistence layer does the rest
I have the following interface that provides me a way to work with cookies:
/// <summary>
/// Provides an interface that makes it easy to work with cookies.
/// </summary>
public interface ICookies
{
#region Properties
/// <summary>
/// Gets or sets the value of the <see cref="ICookies"/>.
/// </summary>
/// <param name="name">The name of the cookie.</param>
/// <returns>A string that represents the value in this cookie.</returns>
string this[string name] { get; set; }
#endregion
#region Methods
/// <summary>
/// Writes a new cookie.
/// </summary>
/// <param name="name">The name of the cookie.</param>
/// <param name="expiration">The <see cref="DateTime"/> when this cookie expires.</param>
/// <param name="value">The value that the cookie should have.</param>
void Create(string name, string value, DateTime expiration);
/// <summary>
/// Checks wether a cookie with a specific name does exist.
/// </summary>
/// <param name="name">The name of the cookie.</param>
/// <returns><see langword="true"/> if the cookie does exists, otherwise, <see langword="false"/>.</returns>
bool DoesExist(string name);
#endregion
}
And I have the following manager that takes the ICookie interface from above to create cookies. And that class looks like the following:
/// <summary>
/// Provides an easy way to work with cookies on the server.
/// </summary>
public static class CookieManager
{
#region Methods
/// <summary>
/// Writes a new cookie on the.
/// </summary>
/// <param name="cookies">The <see cref="ICookies" /> that is responsible for working with cookies.</param>
/// <param name="name">The name of the cookie.</param>
/// <param name="value">The value of the cookie.</param>
public static void Write(ICookies cookies, string name, string value)
{
if (!Exists(cookies, name))
{
cookies.Create(name, value, DateTime.Now.AddYears(1));
}
cookies[name] = value;
}
/// <summary>
/// Reads a cookie.
/// </summary>
/// <param name="cookies">The <see cref="ICookies" /> that is responsible for working with cookies.</param>
/// <param name="name">The name of the cookie to read.</param>
/// <returns>The value of the cookie.</returns>
/// <exception cref="KeyNotFoundException">The cookie is not existing.</exception>
public static string Read(ICookies cookies, string name)
{
return cookies[name];
}
/// <summary>
/// Check if a cookie does exists.
/// </summary>
/// <param name="cookies">The <see cref="ICookies" /> that is responsible for working with cookies.</param>
/// <param name="name">The name of the cookie.</param>
/// <returns><see langword="true" /> when the cookie does exists, otherwise <see langword="false" />.</returns>
public static bool Exists(ICookies cookies, string name)
{
return cookies.DoesExist(name);
}
#endregion
}
Now, I would like to write a Unit test that mocks the cookie interface to make sure it passes.
I would like to check the following:
When I call CookieManager.Write a cookie should be written and I should be able to read it using the CookieManager.Read function. What's the correct approach for that because I've already tried everything (working with the Verifyable, Setup, SetupGet, SetupSet).
Quite important to say is that I'm working with Moq.
Here's what I have for the moment:
I have a constant first:
protected const string CookieReturnValue = "ReturnValue";
Then I have my mock of ICookie:
protected override void Arrange()
{
cookies = new Mock<ICookies>();
cookies.Setup(c => c.Create(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<DateTime>()));
cookies.SetupGet(c => c[It.IsAny<string>()]).Returns(CookieReturnValue).Verifiable();
}
Then the test is executed:
protected override void Act()
{
CookieManager.Write(cookies.Object, "MyCookie", "MyValue");
CookieManager.Read(cookies.Object, "MyCookie");
}
And at the very end, the test is verified:
cookies.Verify();
Now, this test is passing, but I'm not too sure if this is the correct way of testing since in my mock, I'm returning the constant 'CookieReturnValue'. I should be able to set it to the value entered as the second parameter in the 'cookies.Setup()' method, where the 2nd parameter holds the value of the cookie.
The main problem here is that the test is also passing if I'm not writing a cookie. I need to make sure that the test is only passing when I'm writing a cookie and then reading the same cookie again.
Can someone if this is the correct way of testing or where that I should adapt it in order to make sure it's working correctly?
You have actually three tests.
The first one tests if the CookieManager checks if a cookie exists, and when not, call Create, then set the cookie:
var cookies = new Mock<ICookies>();
// there's no cookie
cookies.Setup(c => c.DoesExist(It.IsAny<string>())).Returns(false);
CookieManager.Write(cookies.Object, "MyCookie", "MyValue");
// check if create was called with the right parameters
cookies.Verify(c => c.Create("MyCookie", "MyValue", It.IsAny<DateTime>()));
// check if the cookie was set
cookies.VerifySet(mock => mock["MyCookie"] = "MyValue");
The second one tests if the CookieManager checks if a cookie exists, and when it does, don't call Create, then set the cookie:
// there's a cookie
cookies.Setup(c => c.DoesExist(It.IsAny<string>())).Returns(true);
CookieManager.Write(cookies.Object, "MyCookie", "MyValue");
// check if create was NOT called
cookies.Verify(c => c.Create(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<DateTime>()), Times.Never);
// check if the cookie was set
cookies.VerifySet(mock => mock["MyCookie"] = "MyValue");
The third one tests if the CookieManager attempts to read the cookie:
var cookies = new Mock<ICookies>();
CookieManager.Read(cookies.Object, "MyCookie");
// check if the indexer was used with the right key
cookies.Verify(c => c["MyCookie"]);
Note: simply calling cookies.Verify() does nothing on its own. You either have to provide a delegate to Verify, like I did in my examples, or call VerifyAll to verify all SetUps.
Also, you could make these tests more strict by e.g. using Times.Once or MockBehavior.Strict.
For example, the first test could also be written as:
var cookies = new Mock<ICookies>(MockBehavior.Strict);
// there's no cookie
cookies.Setup(c => c.DoesExist("MyCookie")).Returns(false);
// so one has to be created with the right parameters
cookies.Setup(c => c.Create("MyCookie", "MyValue", It.IsAny<DateTime>()));
cookies.SetupSet(mock => mock["MyCookie"] = "MyValue");
CookieManager.Write(cookies.Object, "MyCookie", "MyValue");
cookies.VerifyAll();
Also, I don't know why the CookieManager sets the cookie value again after it just created it with cookies.Create(...).
Furthermore, there's no need to setup the mock to return the constant CookieReturnValue and then check if that constant is actually returnd my that mock. This would not test the CookieManager, but rather the mock object itself, which is pointless.
I am using an external service for metatagging my pages. I implemented circuit breaker as well as fallback for it and is working perfectly. Now I want to write unit tests for it. Any thoughts?
I am pasting some part of my code for your review:
Unity Registration
.RegisterType<ITaggerRepository>(new InjectionFactory(c => Framework.CrossCutter.ApplyAspectsTo((ITaggerRepository)c.Resolve<CachedTaggerRepository>(), "TaggerRepository").Configure<ISlaAspectConfiguration<ITaggerRepository>>(config => config.Fallback = new TaggerRepositoryFallback())
.Proxy));
Fallback implementation:
public class TaggerRepositoryFallback : ITaggerRepository
{
/// <summary>
/// Requests the tags.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Tagger response</returns>
public TaggerResponse RequestTags(TaggerRequest request)
{
return new TaggerResponse
{
Url = request.PageUrl,