I'm writing an unit test(using NUnit & MOQ) for an action method MethodUnderTest which uses HttpContext internally to do some functionality. I'm setting up a fake hosting environment by calling InitializeHostingEnvironment where I'm initializing the session like:
public static HttpSessionState InitializeSession()
{
var httpRequest = new HttpRequest("", "http://localhost/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
HttpContext.Current = httpContext;
HttpContext.Current.Items.Clear();
HttpSessionState session = (HttpSessionState)ReflectionHelper.Instantiate(typeof(HttpSessionState), new Type[] { typeof(IHttpSessionState) }, new FakeHttpSessionState());
HttpContext.Current.Items.Add("AspSession", session);
return session;
}
public static void InitializeHostingEnvironment(string userName, string password)
{
// lines of code
InitializeSession();
}
I'm calling the InitializeHostingEnvironment() from my Test Method like so:
public static void Test_MethodUnderTest()
{
InitializeHostingEnvironment(UN, PW);
MethodUnderTest(param1, param2, param3); -- getting exception while trying to execute this line
}
While trying to execute the line MethodUnderTest(param1, param2, param3);, I'm getting an exception - System.ArgumentNullException - Value cannot be null. Parameter name httpBrowserCapabilities. Stack trace is given below:
Since the exception says httpBrowserCapabilities is null, I tried to initialize it like HttpContext.Current.Request.Browser = new HttpBrowserCapabilities(); inside the InitializeSession() method, but now, I'm getting another exception:
What should I do now? Is the way I'm initializing HttpContext wrong? please advise.
I'm writing an unit test(using NUnit & MOQ) for a method MethodUnderTest which uses HttpContext internally to do some functionality.
Right. Don't do that. The controller should be the only method that accesses the HttpContext, and it should extract the data needed for your MethodUnderTest and use the to write to the HttpContext.
Structuring your code so that each method has a single responsibility is fundamental to writing testable code.
Related
I want to write a unit test which tests the function of a class called UploadedFile.
The problem I face is this class' static constructor uses HttpContext.Current property and because I am running my unit test from a class library I do not have an HttpContext at the testing time.
Look at my static constructor:
static UploadedFile()
{
if (HttpContext.Current == null)
throw new Exception("web server not available");
HttpServerUtility server = HttpContext.Current.Server;
// SET UploadedFileMappingFile Names:
_resourceFileNames = new StringDictionary();
_resourceFileNames[_suppoertedFileStructures] = server.MapPath(SupportedUploadedFileStructures);
_resourceFileNames[_supportedFileStructuresXSD] = server.MapPath(SupportedUploadedFileStructuresXSD);
_resourceFileNames[UploadedFileEnum.UploadedFileFormatENUM.CSV.ToString()] = server.MapPath(UploadedFileColumnMap);
}
What should I do in my testing environment so that HttpContext.Current won't be null and I can successfully set this:
HttpServerUtility server = HttpContext.Current.Server;
You shouldn't use HttpContext.Current directly in your function as it is close to impossible to unit test, as you've already found out. I would suggest you using HttpContextBase instead, which is passed either in the constructor of your class or as an argument to the method you are testing. This will allow the consumers of this class to pass a real HttpContextWrapper and in your unit test you can mock the methods you need.
For example here's how you could call the method:
var wrapper = new HttpContextWrapper(HttpContext.Current);
Foo.UploadedFile(wrapper);
And in your unit test (using Rhino Mocks):
var contextMock = MockRepository.GenerateMock<HttpContextBase>();
// TODO: Define expectations on the mocked object
Foo.UploadedFile(contextMock);
Or, if you prefer, use Constructor Injection.
In attempts to do some test-driven-development, I've created the most basic, buildable method:
public class NoteService : INoteService
{
public IEnumerable<Annotation> GetNotes(ODataQueryOptions oDataQueryOptions)
{
return new List<Annotation>();
}
}
When trying to unit test it, it seems impossible to create an instance of ODataQueryOptions:
[TestFixture]
public class NoteServiceTests
{
[Test]
public void GetNotes_Returns_IEnumerable_Of_Notes()
{
var sut = new NoteService();
var queryOptions = new ODataQueryOptions(new ODataQueryContext(new EdmCoreModel(), new EdmCollectionType())// new new new etc??
Assert.That(() => sut.GetNotes(options), Is.InstanceOf<IEnumerable<Annotation>>());
}
}
How do you create a simple instance of the object ODataQueryOptions in order to inject it for unit tests?
Will this work?
var request = new HttpRequestMessage(HttpMethod.Get, "");
var context = new ODataQueryContext(EdmCoreModel.Instance, typeof(int));
var options = new ODataQueryOptions(context, request);
I wanted to do something similar and found a workable solution:
My use case
I have a web service that passes OData query parameters down to our CosmosDB document client which translates them into a CosmosDB SQL query.
I wanted a way to write integration tests directly on the CosmosDB client without having to make outgoing calls to other downstream services.
Approaches
Tried mocking ODataQueryParameters using Moq but because it's a class and not an interface, Moq can't properly instantiate all of the properties that I need
Tried instantiating one directly, but outside of an MVC application this is extremely difficult to build the required EdmModel.
Wondered if there is a way to do it without constructing an EdmModel?
I finally figured out a way to do it with the following:
This may not solve every use case, but here's the solution that I landed on for my needs:
public static class ODataQueryOptionsBuilder
{
private static WebApplicationFactory<TEntryPoint> _app =
new WebApplicationFactory<TEntryPoint>();
public static ODataQueryOptions<T> Build<T>(
string queryString)
where T : class
{
var httpContext = new DefaultHttpContext();
httpContext.Request.QueryString = new QueryString(queryString);
httpContext.RequestServices = _app.Services;
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntityType<T>();
var model = modelBuilder.GetEdmModel();
var context = new ODataQueryContext(
model,
typeof(T),
new Microsoft.AspNet.OData.Routing.ODataPath());
return new ODataQueryOptions<T>(context, httpContext.Request);
}
}
Conclusion
I realized that all I needed to get the ODataQueryOptions was a test server that was set up exactly like my web service. Once I realized that, the solution was simple. Use WebApplicationFactory to create the test server, and then allow OData to use the IServiceProvider from that application to build the ODataQueryOptions that I needed.
Drawbacks
Realize that this solution is as performant as your TEntryPoint, so if your application takes a long time to start up, this will cause unit/integration tests to take a long time to run.
I have a routine something like this:
public bool IsValidEmployee(string email, string password)
{
bool valid = false;
var employee = dataAccess.GetEmployee(email, password);
if(employee! = null)
{
valid = true;
HttpContext.Current.Session["Employee"] = employee;
}
return valid;
}
My unit test:
[TestMethod()]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("C:\Projects", "/")]
[UrlToTest("http://localhost:59349/")]
public void GetEmployeeTest()
{
Domain target = new Domain();
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.SetupSet(c => c.Session["Employee"] = It.IsAny<object>());
Assert.IsTrue(target.IsValidEmployee("sam#gmail.com", "test");
}
The code fails as
Object Null Reference on 'HttpContext.Current.Session["Employee"] = employee;'
Any suggestions how i can fix this error?
I don't believe just mocking out HttpSession is enough to get the session in your method to take the mocked behavior. You need a way to inject that dependency.
You could redesign your function to take in the session object as a param. This would make your method testable
For example
public bool IsValidEmployee(string email, string password, HttpSessionStateBase session)
{
bool valid = false;
var employee = dataAccess.GetEmployee(email, password);
if(employee! = null)
{
valid = true;
session["Employee"] = employee;
}
return valid;
}
Additionally you could create a "SessionManager" that could impliment ISessionManager that would wrap all your access to session state and pass that around making it even more testable thus decoupling the responsibility of how and where to persist session state from Validating an Employee.
Moles will allow you to intercept and substitute the calls to session.
http://research.microsoft.com/en-us/projects/pex/getstarted.pdf
Using session should be avoided if at all possible due to the burden it places on the server.
That code looks pretty error prone, but maybe that's why you are adding unit tests.
I think you also need to mock getter for the session...
public void GetEmployeeTest()
{
Domain target = new Domain();
var mockHttpContext = new Mock<HttpContextBase>();
var mockSession = new Mock<HttpSessionStateBase>();
mockHttpContext.SetupGet(c => c.Session).Returns(mockSession.Object);
mockHttpContext.SetupSet(c => c.Session["Employee"] = It.IsAny<object>());
Assert.IsTrue(target.IsValidEmployee("sam#gmail.com", "test"));
}
Which code fails? Is this a test failure due to an error in your method, or is the occurring error in the test code itself?
Regardless of how you might improve your test (and this may come across as sounding a bit obvious), but I wonder if you have set a break-point on the setup line in your test method, and actually checked that the object you are trying to assign a value to is null? If this is the case, you need to check your setup, and as others may have said you'll probably need to mock your session in such a way as to ensure the setup portion of your test won't fail.
Cheers.
I am attempting to create integration tests to make sure my views do not have any runtime errors in them. Thus I need to create a test that checks if ViewResult.ExecuteResult() works correctly but it seems I have hit a snag.
I found this site which gave me a starting point, and I have the following code:
[TestMethod]
public void RegisterResultExecutes()
{
//arrange
RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
AccountController controller = new AccountController
{
FormsService = new MockFormsAuthenticationService(),
MembershipService = new MockMembershipService(),
Url = new UrlHelper(requestContext)
};
var result = controller.Register();
var sb = new StringBuilder();
Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
{
sb.Append(y);
});
Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);
//act
result.ExecuteResult(controllerContext.Object);
}
The problem is that when result.ExecuteResult() is called I get the following exception
System.NullReferenceException: Object reference not set to an instance of an object.
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
MyApp.Tests.Controllers.AccountControllerTest.RegisterResultExecutes() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.Tests\Controllers\AccountControllerTests.cs: line 297
Unfortunately, that stack trace isn't very useful as I'm not sure what it's trying to access that is null. Does anyone have any suggestions on how I can create a test for ExecuteResult()?
Based on the stack trace, it is something in the ViewResultBase.ExecuteResult method that throws the exception. Using reflector, here is the definition of that method:
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (string.IsNullOrEmpty(this.ViewName))
{
this.ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result = null;
if (this.View == null)
{
result = this.FindView(context);
this.View = result.View;
}
TextWriter output = context.HttpContext.Response.Output;
ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
this.View.Render(viewContext, output);
if (result != null)
{
result.ViewEngine.ReleaseView(context, this.View);
}
}
Based on that code, an object reference exception could be thrown when the code tries to access the RouteData property from the context (if the name of the view wasn't explicitly given to the return type).
The exception could be thrown by accessing the HttpContext property. I haven't used Moq well enough to know if it can handle the fact that you haven't told it how to mock the HttpContext property, but you have told it how to mock the Response property from the HttpContext property's type, so that's another area that is suspect to me.
All other uses of context in the method are passing it into other methods, which if those were the problem, then the stack trace would have revealed that.
The easiest way to see which of the two I mentioned are the problem, I would write a quick test to pull those properties from your mocks and see which one causes the exception.
I hit the same problem as this just now and resolved it by setting the HttpContext.Current.
Try adding the following to your unit test code: e.g.
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://mock", ""),
new HttpResponse(new StringWriter()));
One thing that I found useful for debugging this sort of problem rather than using reflector or ILSpy is to turn on debug symbols for the .Net framework code. That way you can attach to your NUnit process and see exactly what line of code is throwing the exception and therefore what you need to Mock in the test.
Shawn Burke has written an excellent blog article detailing how to set this up here: http://blogs.msdn.com/b/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx
We're using ELMAH for handling errors in our ASP.Net MVC c# application and in our caught exceptions, we're doing something like this:
ErrorSignal.FromCurrentContext().Raise(exception);
but when I try to unit test the caught exceptions, I get this message:
System.ArgumentNullException: Value cannot be null.
Parameter name: context
How can I mock the FromCurrentContext() call?
Is there something else I should be doing instead?
FYI... We're currently using Moq and RhinoMocks.
Thanks!
Since the FromCurrentContext() method is a static method you can't simply mock the call to it. You do have two other options.
Since FromCurrentContext() internally makes a call to HttpContext.Current you can push a fake context in that. For example:
SimpleWorkerRequest request = new SimpleWorkerRequest(
"/blah", #"c:\inetpub\wwwroot\blah", "blah.html", null, new StringWriter());
HttpContext.Current= new HttpContext(request);
With this it should not throw the exception anymore since HttpContext.Current is not null.
Create a wrapper class around the call to Raise and just mock out the wrapper class.
public class ErrorSignaler {
public virtual void SignalFromCurrentContext(Exception e) {
if (HttpContext.Current != null)
Elmah.ErrorSignal.FromCurrentContext().Raise(e);
}
}