I'm using C#4.0 and i need to unit test a service. The function inside the service returns a path similar to the variable i called expected, this is the path i'm expecting to get back. But when i run this test i'm getting the error that HttpContext.Current is NULL. What can i do to fix this issue so the test can be ran?
[TestMethod]
public void GetPathTest()
{
var expected = System.IO.Path.GetFullPath(HttpContext.Current.Server.MapPath("~/Certificates/"));
var path = _mockService.Setup(o => o.GetPath()).Returns(expected);
}
At the moment I can't find my full wrapper for HttpContext that I used earlier, but at the moment we simply create a context for an empty request and go from there, like this:
SimpleWorkerRequest request = new SimpleWorkerRequest("","","", null, new StringWriter());
HttpContext context = new HttpContext(request);
Then in the unit test initialize or in the unit test itself (before you create expected) you can set the current HttpContext as follows:
HttpContext.Current = context;
Then simply flesh out the fake context and possible fake sessionstate, etc as required.
(Edit: This is all in VS2008, framework 3.5 by the way).
You can try looking at the attributes created for ASP.Net unit testing, like
[HostType("ASP.NET")]
This link to MSDN has quite a good write-up about it
You could decorate your test method with the followings attributes:
[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://localhost:xxxx/")]
[AspNetDevelopmentServerHost("$(SolutionDir)\\xxx\\", "/")]
public void TestMethod()
{
...
}
Then adding a Default.aspx file into your unit test proj.
Inside the test method you can easily access to the HttpContext.
If you want to debug, you may use some traces or interrupt the debugging execution with the instruction System.Diagnostics.Debugger.Break()
public void TestMethod()
{
System.Diagnostics.Debugger.Break();
...
}
and then attaching debugger to the process as explained by MSDN:
https://msdn.microsoft.com/en-us/library/vstudio/c6wf8e4z(v=vs.100).aspx
I'm posting this for reference. It's not an easy solution, and talks about Duck Typing (if it quacks..):
http://haacked.com/archive/2007/08/19/why-duck-typing-matters-to-c-developers.aspx
http://haacked.com/archive/2007/09/09/ihttpcontext-and-other-interfaces-for-your-duck-typing-benefit.aspx
It's relevant and worth a read; because there's no IHttpContext it's not possible to create a test environment implementation - until you consider using the Duck Typing library here. Though this is not a direct answer.
Hope that helps.
Related
I am new to Web API. I have inherited code with unit tests written in c#. However, when I run unit tests the tests pass irrespective of the controller name. For ex: http://localhost/api/users -> even if users is misspelled the unit test passes.
I have the following code in the controller:
public async Task TestGetUsers()
{
usersController controller = new usersController(new TestUserRepository());
ApiControllerConfig.SetupControllerForTest(controller, "http://localhost/api/users", HttpMethod.Get);
IHttpActionResult result = controller.Get();
HttpResponseMessage message = await result.ExecuteAsync(CancellationToken.None);
string content = await message.Content.ReadAsStringAsync();
Assert.IsTrue(content.Length > 0);
}
Not sure if this information is sufficient to tell me where the problem lies but please let me know if more information needs to be posted.
You're reading the content from a 404 not found request: that will still have content (to tell you the content could not be found). Therefore asserting that the content length is over 0 makes no sense because it doesn't differentiate between a failed and a passed test.
What you should do is look at the statuscode (message.StatusCode) and make sure that is HttpStatusCode.Ok.
I would also advice to just add the Web Api project as a reference to your unit test project -- that way you can call the methods directly rather than having to go through a HttpClient. For more on this (including testing your routes), take a look at my blog.
you could use directly the apicontroller in the test project, just add the reference and test it.
Something like this:
public void TestMethod()
{
var ctr = new MyApiController();
string name = ctr.GetName();
Assert.IsTrue(name == "xpto");
}
I have this weird problem when trying to mock an interface with MockedClass.Setup(x => x.Method()).
This is the interface I'm mocking.
public interface IClassFactory
{
object GetValueFromFormData(string key, MultipartFormDataStreamProvider provider);
}
This is my test.
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task ClassApiController_ImportClassList_ThrowsNullReferenceExceptionWhenNoClassId()
{
// Arrange
_classFactory = new Mock<IClassFactory>();
// THIS IS THE LINE THAT GIVES AN EXCEPTION
_classFactory.Setup(x => x.GetValueFromFormData("classIdNull", null)).Returns(string.Empty);
ClassApiController controller = new ClassApiController(_classRepository.Object, _surveyRepository.Object, _classFactory.Object);
// Act
string result = await controller.ImportClassList();
}
If you look at my comment "THIS IS THE LINE THAT GIVES AN EXCEPTION" you see I send null, but it doesn't matter if I send the MultipartFormDataStreamProvider as an instansiated class instead, I still get the same exception.
Exception message: System.ArgumentException: Expression of type 'System.Net.Http.MultipartFormDataStreamProvider' cannot be used for parameter of type 'System.Net.Http.MultipartFormDataStreamProvider' of method 'System.Object GetValueFromFormData(System.String, System.Net.Http.MultipartFormDataStreamProvider)'
If you know why I can't mock the method just because it has this object as parameter, please help me, I'm clueless.
Thanks!
EDIT:
See solution in my answer
you should try
_classFactory = Mock.Of<IClassFactory>();
Mock.Get(_classFactory).Setup(x => x.GetValueFromFormData("classIdNull", It.IsAny<MultipartStreamProvider>()))
.Returns(string.Empty);
With the help of #Vignesh.N I finally solved it. The simple answer in this case was that my solution is divided into several projects. Web, Test, Data and so on. In the web project I had referenced the web api .dll:s via Nuget and in the test project I had referenced them directly via Add references -> Assemblies -> Framework. Thereby the .dll:s had identical Versions but not File versions. After making Nuget take care of all the projects web api .dll files it worked instantly.
So all in all, stupid mistake and hard to spot.
I have been unsuccessful in attempting to set a session variable in order to run some unit tests. I keep getting the error of "System.NullReferenceException: Object reference not set to an instance of an object" when I attempt to set my session variable.
Here is the test I'm building:
[TestMethod]
public void MyMethod()
{
//Arrange
int id = 12345;
string action = "A";
string comment = "";
string user = "user";
var controller = new MyController();
//Act
controller.Session["altUser"] = user;
var result = controller.Process(id, action, comment);
//Assert
Assert.IsNotNull(result);
}
And here is my controller:
[Authorize]
public class MyController : Controller
{
public ActionResult Process(int id, string action, string comment)
{
string userId = Session["altUser"].ToString();
//some other stuff that evaluates ID, Action, and Comment
}
}
However, when I run the application itself, there are no errors and the application functions as it should. I do understand that with Test Driven Development the tests should pave the way for the implementation. I am trying to get some practice with Unit Testing on applications that have already been done. If at all possible, since the application works, I would like to avoid making any changes to my implementation and just write a unit test to support what I already know.
The controller gets the session from the HttpContext, which does not exist in your unit test, which is why it fails.
You can, however, mock an HttpContext and put a mock session in there too.
Something like this might work (uses moq as the Mocking framework)
var mockControllerContext = new Mock<ControllerContext>();
var mockSession = new Mock<HttpSessionStateBase>();
mockSession.SetupGet(s => s["altUser"]).Returns("user");
mockControllerContext.Setup(p => p.HttpContext.Session).Returns(mockSession.Object);
var controller = new MyController();
controller.ControllerContext = mockControllerContext.Object;
You'll obviously need to fill the mock object with the details of what you actually want to get out.
You can also derive your own classes from HttpSessionStateBase and HttpContextBase and use them instead of the real session.
There is no spoon, er, session — the ASP.NET runtine is neither initialized nor running when you execute the unit test. You should decompose any Session dependencies outside your code and make the classes and methods you need to unit test independent of ASP.NET's runtime features.
When you run the unit test, you're not running the program via a web server, and therefore do not have access to any HttpContext, or ASP.NET session, whereas when you run the program via debug in VS, you are using the built in Visual Studio web server, which enables the program to use the session.
Here is an article which walks you through the ASP.NET session and how it works.
I'm still trying to add in some unit tests for an ASP.NET site (not MVC). One of the methods I need to test makes use of the HttpRequest Request object, Request.Path to be exact. I'm trying to write the tests using Visual Studio 2008's built in testing framework. Whenever the test executes the method in question, I receive a System.Web.HttpExecption: Request is not Available in this context. I understand why it is not available (there is no running web server and not path supplied), but how can I proceed with testing the method?
Since everyone likes to see code, here's the code in question:
protected string PageName
{
get
{
return Path.GetFileName(Request.Path).Substring(0, Path.GetFileName(Request.Path).Length - 5);
}
}
protected Change SetupApproval(string changeDescription)
{
Change change = Change.GetInstance();
change.Description = changeDescription;
change.DateOfChange = DateTime.Now;
change.Page = PageName;
return change;
}
Here's the test:
[TestMethod]
public void SetupApproval_SubmitChange_ValidateDescription()
{
var page = new DerivedFromInternalAppsBasePage_ForTestingOnly();
var messageToTest = "This is a test description";
var change = page.SetupApproval(messageToTest);
Assert.IsTrue(messageToTest == change.Description);
}
In addition, I've read through Microsoft's documentation here: http://msdn.microsoft.com/en-us/library/ms182526(v=vs.90).aspx
and tried using the [HostType("ASP.NET")], [UrlToTest(http://localhost:port/pathToAspxPage.aspx")], and [AspNetDevelopmentServer("C:\PathToDllAssembly", "NotSureParameter")] Attributes they suggest, but no luck. (As you can see, I'm not sure about what I should be using for a few of the parameters.).
Lastly, I tried Phil Haack's TestWebServer http://haacked.com/archive/2006/12/12/Using_WebServer.WebDev_For_Unit_Tests.aspx
and read through Scott Hanselman's post http://www.hanselman.com/blog/NUnitUnitTestingOfASPNETPagesBaseClassesControlsAndOtherWidgetryUsingCassiniASPNETWebMatrixVisualStudioWebDeveloper.aspx
For Phil's server, I'm not sure what I would use for parameters in the ExtractResource method.
I've been struggling with similar problems recently and what was really helpful is the Moles framework from Microsoft Research - http://research.microsoft.com/en-us/projects/moles/. It allows you to fake anything from BCL including HttpContext.Current.
There is a very similar problem to the one you have encountered which is described in the book "Working Effectively with Legacy Code" by Michael Feathers. In particular, the refactoring is called "Adapt Parameter".
The "problem" with your code is that it is directly coupled to the HttpRequest, specifically Request.Path. So the overall approach is you want to decouple your code from HttpRequest.
Similarly to what is suggested above, here is another way to do the decoupling following the idea in Michael Feather's book. I haven't tried compiling this, so please excuse any typos or syntax errors.
public interface ParameterSource
{
public string Path {get; }
}
public class FakeParameterSource : ParameterSource
{
public string Value;
public string Path { get { return Value } }
}
public class RealParameterSource : ParameterSource
{
private HttpRequest request;
public RealParameterSource(HttpRequest aRequest)
{
request = aRequest;
}
public string Path { get { return request.Path } }
}
Now here's the important part of what you need to change (and here's one way of doing it):
// small rename
protected string GetPageName(ParameterSource source)
{
return Path.GetFileName(source.Path).Substring(0, Path.GetFileName(source.Path).Length - 5);
}
Above the injection happens at the method level. You could do it via a constructor or a property too.
Your test code could now be something like:
protected Change SetupApproval(string changeDescription)
{
ParameterSource p = new FakeParameterSource { Value = "mypath" };
Change change = Change.GetInstance();
change.Description = changeDescription;
change.DateOfChange = DateTime.Now;
change.Page = GetPageName(p); // now uses the value in the parameter source
return change;
}
I hope you get the idea and find this useful
The key to testing methods that use HttpContext is to use HttpContextBase. HttpContextBase is an abstract class which makes it easily mockable. Unfortunately HttpContext does not implement HttpContextBase so you need to use HttpContextWrapper to wrap the real HttpContext to HttpContextBase. This article has a good and simple explanation: http://splinter.com.au/httpcontext-vs-httpcontextbase-vs-httpcontext
Your question doesn't say where the example code is located. If it's all in the code-behind you are going to have a difficult time trying to test that. Instead of having logic in code-behind, you should look into Model-View-Presenter(MVP) -pattern. There are frameworks for the MVP, but it is also quite easy to do it yourself.
In MVP presenters contain all the logic and pages code-behind only implements View-interface containing properties which can be bound to the UI. All dependencies can be injected to presenter making it easy to test.
I agree with #Alex Taylor (sorry, I can't comment): the best way of unit testing a (non-MVC) web app is to make sure the bits don't rely on the whole environment being present; that's why it's called unit testing, after all. So you could replace PageName with this:
protected string PageName
{
get
{
return GetPageName(Request.Path);
}
}
protected static string GetPageName(string path)
{
return Path.GetFileNameWithoutExtension(path);
}
And with this you can unit test GetPageName, and be confident that PageName works as expected.
Remember, there's no point trying to unit test Request.Path, because that has an expected behaviour (or 'contract') that it should fulfil. If you're not sure what Request.Path will return in different circumstances, you should check the documentation instead of adding unit tests.
I am testing code in a MVC HTML helper that throws an error when trying to get the application path:
//appropriate code that uses System.IO.Path to get directory that results in:
string path = "~\\Views\\directory\\subdirectory\\fileName.cshtml";
htmlHelper.Partial(path, model, viewData); //exception thrown here
The exception that is thrown is
System.Web.HttpException: The application relative virtual path '~/Views/directory/subdirectory/fileName.cshtml' cannot be made absolute, because the path to the application is not known.
Following the advice of How to resolve issue with image path when testing HtmlHelper?
I have faked (using Moq):
Request.Url to return a string
Request.RawUrl to return a string
Request.ApplicationPath to return a string
Request.ServerVariables to return a null NameValueCollection
Response.ApplyAppPathModifier(string virtualPath) to return a string
What else is needed to be able to allow this code to run in the context of a unit test run?
Or
What other approach should I be taking to render a Partial view on a dynamically built string?
As an alternative to mocking built-in .net classes, you can
public interface IPathProvider
{
string GetAbsolutePath(string path);
}
public class PathProvider : IPathProvider
{
private readonly HttpServerUtilityBase _server;
public PathProvider(HttpServerUtilityBase server)
{
_server = server;
}
public string GetAbsolutePath(string path)
{
return _server.MapPath(path);
}
}
Use the above class to get absolute paths.
And for For unit testing you can mock and inject an implementation of IPathProvider that would work in the unit testing environment.
--UPDATED CODE
For what it's worth, I ran up against the same error and followed it through the System.Web source to find it occurs because HttpRuntime.AppDomainAppVirtualPathObject is null.
This is an immutable property on the HttpRuntime singleton, initialized as follows:
Thread.GetDomain().GetData(key) as String
where key is ".appVPath". i.e. it comes from the AppDomain. It might be possible to spoof it with:
Thread.GetDomain().SetData(key, myAbsolutePath)
But honestly the approach in the accepted answer sounds much better than mucking around with the AppDomain.
I'm including a solution from a blog post, which is no longer available (http://blog.jardalu.com/2013/4/23/httprequest_mappath_vs_httpserverutility_mappath)
Complete code: http://pastebin.com/ar05Ze7p
Ratna (http://ratnazone.com) code uses "HttpServerUtility.MapPath" for
mapping virtual paths to physical file path. This particular code has
worked very well for the product. In our latest iteration, we are
replacing HttpServerUtility.MapPath with HttpRequest.MapPath.
Under the hoods, HttpServerUtility.MapPath and HttpRequest.MapPath are
the same code and will result in the same mapping. Both of these
methods are problematic when it comes to unit testing.
Search for "server.mappath null reference" in your favourite search
engine. You are going to get over 10,000 hits. Almost all of these
hits are because test code calls HttpContext.Current and
HttpServerUtility.MapPath. When the ASP.NET code is executed without
HTTP, HttpContext.Current will be null.
This issue (HttpContext.Current is null) can be solved very easily by
creating a HttpWorkerRequest and intializing HttpContext.Current with
that. Here is the code to do that -
string appPhysicalDir = #"c:\inetpub\wwwroot";
string appVirtualDir = "/";
SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter());
HttpContext.Current = new HttpContext(request);
With that simple code in unit test, HttpContext.Current is
initialized. Infact, if you notice, HttpContext.Current.Server
(HttpServerUtility) will also be intiailzed. However, the moment, the
code tries to use Server.MapPath, the following exception will get
thrown.
System.ArgumentNullException occurred
HResult=-2147467261
Message=Value cannot be null.
Parameter name: path
Source=mscorlib
ParamName=path
StackTrace:
at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
InnerException:
HttpContext.Current = context;
Infact, if the code uses HttpContext.Current.Request.MapPath, it is
going to get the same exception. If the code uses Request.MapPath, the
issue can be resolved in the unit test easily. The following code in
unit test shows how.
string appPhysicalDir = #"c:\inetpub\wwwroot";
string appVirtualDir = "/";
SimpleWorkerRequest request = new SimpleWorkerRequest(appVirtualDir, appPhysicalDir, "/", null, new StringWriter());
FieldInfo fInfo = request.GetType().GetField("_hasRuntimeInfo", BindingFlags.Instance | BindingFlags.NonPublic);
fInfo.SetValue(request, true);
HttpContext.Current = new HttpContext(request);
In the above code, the request worker will be able to resolve the map
path. This is not enough though, because HttpRequest does not have the
HostingEnvironment set (which resolves MapPath). Unfortunately,
creating a HostingEnvironment is not trivial. So for unit-test, a
"mock host" that just provides the MapPath functionality is created.
Again, this MockHost hacks lot of internal code. Here is the
pseudo-code for the mock host. Complete code can be downloaded here:
http://pastebin.com/ar05Ze7p
public MockHost(physicalDirectory, virtualDirectory){ ... }
public void Setup()
{
Create new HostingEnvironment
Set Call Context , mapping all sub directories as virtual directory
Initialize HttpRuntime's HostingEnvironment with the created one
}
With the above code when MapPath is called on HttpRequest by it should
be able to resolve the path.
As a last step, in the unit test, add the following code -
MockHost host = new MockHost(#"c:\inetpub\wwwroot\", "/");
host.Setup();
Since now a HostingEnvironment has been initialized, the test code
will be able to resolve virtual paths when
HttpContext.Current.Request.MapPath method is called (along with
HostingEnvironment.MapPath and HttpServerUtility.MapPath).
Download MockHost code here: http://pastebin.com/ar05Ze7p
Trying to make parts of ASP.NET happy with various types of tests seems, to me, to be quite fragile. And I am inclined to believe that the mocking route only works if you basically avoid using ASP.NET or MVC and, instead, write your own webserver from scratch.
Instead, just use ApplicationHost.CreateApplicationHost to create a properly-initialized AppDomain. Then run your test code from within that domain using AppDomain.DoCallback.
using System;
using System.Web.Hosting;
public class AppDomainUnveiler : MarshalByRefObject
{
public AppDomain GetAppDomain()
{
return AppDomain.CurrentDomain;
}
}
public class Program
{
public static void Main(string[] args)
{
var appDomain = ((AppDomainUnveiler)ApplicationHost.CreateApplicationHost(
typeof(AppDomainUnveiler),
"/",
Path.GetFullPath("../Path/To/WebAppRoot"))).GetAppDomain();
try
{
appDomain.DoCallback(TestHarness);
}
finally
{
AppDomain.Unload(appDomain);
}
}
static void TestHarness()
{
//…
}
}
Note: when trying this myself, my test runner code was in a separate assembly from the WebAppRoot/bin directory. This is an issue because, when HostApplication.CreateApplicationHost creates a new AppDomain, it sets its base directory to something like your WebAppRoot directory. Therefore, you must define AppDomainUnveiler in an assembly that is discoverable in the WebAppRoot/bin directory (so it must be in your webapp’s codebase and cannot be stored separately in a testing assembly, unfortunately). I suggest that if you want to be able to keep your test code in a separate assembly, you subscribe to AppDomain.AssemblyResolve in AppDomainUnveiler’s constructor. Once your testing assembly gets the AppDomain object, it can use AppDomain.SetData to pass along information about where to load the testing assembly. Then your AssemblyResolve subscriber can use AppDomain.GetData to discover where to load the test assembly from. (I’m not sure, but the sort of objects you can SetData/GetData might be quite limited—I’ve just used strings myself to be safe). This is a bit annoying, but I think it is the best way to separate concerns in this situation.
This happens once you login to the application and you try to add any new url to the http context and trying to create SimpleWorkerRequest.
in my case i have an url to get the documents from remote server and added the url to http context and trying to authenticate the user and create the SimpleWorkerRequest.
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
var moqRequestContext = new Mock<RequestContext>(MockBehavior.Strict);
request.SetupGet<RequestContext>(r => r.RequestContext).Returns(moqRequestContext.Object);
var routeData = new RouteData();
routeData.Values.Add("key1", "value1");
moqRequestContext.Setup(r => r.RouteData).Returns(routeData);
request.SetupGet(x => x.ApplicationPath).Returns(PathProvider.GetAbsolutePath(""));
public interface IPathProvider
{
string GetAbsolutePath(string path);
}
public class PathProvider : IPathProvider
{
private readonly HttpServerUtilityBase _server;
public PathProvider(HttpServerUtilityBase server)
{
_server = server;
}
public string GetAbsolutePath(string path)
{
return _server.MapPath(path);
}
}