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 am building a test for an MVC5 controller method. I'm using moq for the test. What I'm interested in is how to test a controller method that requires authentication and uses the userid value not the username value to make decisions about what data to show to the browser/client.
From what I have researched so far, there is a considerable amount of code available to moq the username, but not much code for the userid value (in my case that looks like a Guid). I'm using ASP.Net Identity as the account management. I have OWIN added as well for Google and Facebook login.
I'm using dependency injection (using Unity) because it seems that that is about the only way to accomplish tests, plus DI enables the use of moq.
Here is an example of the test method that is looking for a 'NotNull' return from the controller method:
Mock<ModelObject> CreateModelObjectFromHelper()
{
var ci = new Mock<ClaimsIdentity>();
var myHelper = new Mock<MyHelper>();
myHelper.Setup(x => x.GetCurrentUserId(ci.Object)).Returns("333c188b-b33a-4233-83bd-5ea3a3333333");
return new Mock<ModelObject>(myHelper.Object);
}
[TestMethod]
public async Task ExampleController_Method_NotNull()
{
Mock<ModelObject> o = CreateModelObjectFromHelper();
ExampleController controller = new ExampleController(o.Object as IModelObject);
ViewResult result = await controller.MethodName() as ViewResult;
//check for non null result
Assert.IsNotNull(result);
}
The GetCurrentUserId method is where I query the User.Identity object for the userid using this code:
var userIdClaim = user.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
userIdValue = userIdClaim.Value.ToString();
And the method is supposedly getting replaced by moq. The GetCurrentUserId method is a virtual method in a helper class (MyHelper) that is the parameter of the constructor for the model object (ModelObject) that is the parameter of the constructor for the controller (ExampleController).
What happens in the debugger (Visual Studio 2013) for the test method is that the objects are created in the CreateModelObjectFromHelper method, but when the ExampleController is created at the line:
ExampleController controller = new ExampleController(o.Object as IModelObject);
The MyHelper moq'd object becomes null. I can see the object has a value before this statement, but at this statement, the MyHelper object becomes null...and the whole test fails. I assume it fails because it is difficult to call methods on an object that is null.
The question is...what causes this object to become null?
The other question is...maybe there is a better way to do this and if so, I'd sure be glad to hear a suggestion.
Thank you
It doesn't work like that. You try to create mock of a mock of a mock ...
If your controller depend only on the IModelObject, then only create Mock and set it's properties/methods to return what you need in the controller.
Without the code for IModelObject, and how it is used in the controller, it's hard to provide better example.
I'm using VS unit testing on an MVC-5, EF-6 project. For now, I just want to get my unit tests to work by having my methods connect to the actual database just like they normally would. I've read a lot about whether or not you should mock the database for purposes of unit testing so it's not necessary to tell me your stance on that.
I have a unit test like this:
[TestMethod]
public void SelectAssessmentTypeWhenLoggedIn()
{
// Arrange - login (otherwise it won't let you do this)
var c = new AccountController();
c.Login("username", "password");//I'm not showing you the real ones
var abc = new AssessmentBuilderController();
// Act - call the SelectAssessmentType() method
ActionResult result = abc.SelectAssessmentType();
//Assert - model should have 4 ATs in it
Assert.AreEqual(((result as ViewResult).Model as IEnumerable<AssessmentType>).Count(), 4);
}
When called from the application, the method always has 4 entries in its model (as it should btw). But when run from the unit test, it always has 0. Here's the method's code:
private List<AssessmentType> GetAssessmentTypes()
{
using (var context = new CLASSContext()) {
return context.AssessmentType.ToList();
}
}
I can debug the unit test and step through the code and see the context is created and the table is in the context and see the SQL statement to be executed AND YET the model (List of AssessmentTypes) has 0 entries afterward! What is going on?
Test projects have their own app.config configuration file - the connection string should be changed there. Web.config is for web projects.
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);
}
}
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.