UrlHelper's RouteUrl returning Empty String in Tests - asp.net-mvc-2

I am having an issue where UrlHelper's RouteUrl method only returns an empty string when run in my tests, though function properly when executing in the real HttpContext. It is, however, finding the route - as I do properly get an exception if I try to resolve a route name which has not been defined.
I have mocked the HttpContext and friends using the code provided by Scott Hanselman/Kzu and added the code needed to bootstrap the Application's Routes into the mocked instance
To reduce the number of variables in my situation, I've written a simple test:
[Test]
public void UrlHelperReturnsCorrectUrl()
{
var controller = new MyController();
controller.SetFakeControllerContext().LoadUrlHelper();
Assert.AreEqual("My/Route/Path", controller.Url.RouteUrl("MyRoute"));
}
Interestingly enough, accessing the RouteCollection directly and using VirtualPath does work:
[Test]
public void GetVirtualPathReturnsCorrectUrl()
{
var controller = new AccountController();
controller.SetFakeControllerContext().LoadUrlHelper();
Assert.AreEqual("My/Route/Path",
Controller.Url.RouteCollection["MyRoute"]
.GetVirtualPath(
controller.Url.RequestContext,
new RouteValueDictionary())
.VirtualPath);
}
For reference, Here is my implementation of the LoadUrlHelper extension method:
public static Controller LoadUrlHelper(this Controller controller)
{
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
controller.Url = new UrlHelper(
controller.ControllerContext.RequestContext,
routes);
return controller;
}
And here is my route as defined in my application's Global.asax:
routes.MapRoute(
"MyRoute", "My/Route/Path",
new {controller = "Home", action = "Index"});
Has anyone run into this? Am I missing something?
EDIT:
I've followed the MVC code down to the point that it hands the route processing off to System.Routing and found something very interesting. The code that MVC eventually runs to lookup the desired URL (condensed, of course) returns an empty string:
Controller.Url.RouteCollection.GetVirtualPath(
Controller.Url.RequestContext,
"MyRoute", new RouteValueDictionary()).VirtualPath;
whereas an extremely similar variant returns the expected string:
Controller.Url.RouteCollection["MyRoute"].GetVirtualPath(
Controller.Url.RequestContext,
new RouteValueDictionary()).VirtualPath;
I can't seem to go any further in the underlying code to see what is actually happening differently here, but thought it might help someone understand what setup I am missing. (I'm not going to yell bug yet, as the fact stands that the UrlHelpers do work when in a real HttpContext)

The solution to my problem was already posted to another SO question.
I had tried to incorporate this solution piece-meal earlier but did so poorly. Once I copied it entirely and modified for my situation, it worked perfectly.
Here is a more generic version that can be reused across many tests (if placed in a base test fixture class or something similar).
Usage:
var controller = GetController<MyController>();
controller.MyAction();
//...
Method:
protected T GetController<T>() where T : Controller, new()
{
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());
var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string p) => p);
var context = new Mock<HttpContextBase>(MockBehavior.Strict);
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
var controller = new T();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
return controller;
}

Related

Register more than one mock or service using AutoMock.GetLoose() (Autofac.Extras.Moq)

I'm in the process of upgrading our Autofac.Extras.Moq library to the latest version (6.0.0) within our Unit Test project. After upgrading, I noticed tests using: var mock = AutoMock.GetLoose(), no longer supported the "Provide" method. So I started digging into the documentation for some sort of workaround.
After taking a look at the Getting Started docs (https://autofaccn.readthedocs.io/en/v5.2.0/integration/moq.html#getting-started) I've noticed there is a new way of registering mocks and dependent services using AutoMock.GetLoose(cfg => cfg.RegisterMock(mockA)). However, some of our tests require more than one Mock injected, and it's not clear to me how to do this.
Take for example:
[Test]
public void Test()
{
var mockA = new Mock();
mockA.Setup(x => x.RunA());
var mockB = new Mock();
mockB.Setup(x => x.RunB());
// mockA is automatically registered as providing IServiceA
using (var mock = AutoMock.GetLoose(cfg => cfg.RegisterMock(mockA)))
{
// mockA will be injected into TestComponent as IServiceA
var component = mock.Create();
// ...and the rest of the test
}
}
How would I register both mockA and mockB?
Thanks.
Have you tried putting both registrations in the GetLoose lambda?
[Test]
public void Test()
{
var mockA = new Mock();
mockA.Setup(x => x.RunA());
var mockB = new Mock();
mockB.Setup(x => x.RunB());
// Register both mocks here:
using (var mock = AutoMock.GetLoose(cfg => {
cfg.RegisterMock(mockA);
cfg.RegisterMock(mockB);
}))
{
// ...and the rest of the test
}
}
If you tried this and it didn't work, you should update your question to include both:
That you tried it AND
What the exception message or incorrect result was
Otherwise, if this works... 🎉

Unit testing With Entity Framework 7, Test fails sometimes?

I have a bunch of test where I use the new UseInMemory function in EF7. When I run them all some of them fail. When I run them single they all pass.
My best guess it is a conflict in EF7 because of the fact that every test runs in its own thread and they all kind of using the same DbContext class.
Here one of my Tests:
[Fact]
public void Index()
{
DbContextOptionsBuilder<DatabaseContext> optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
optionsBuilder.UseInMemoryDatabase();
db = new DatabaseContext(optionsBuilder.Options);
AdminController controller = new AdminController(db);
var result = controller.Index() as ViewResult;
Assert.Equal("Index", result.ViewName);
}
I remake the dbContext object in every test but it seem not to make any different.
Would be greatful for any input. Thanks :)
The problem is, that the memory storage in InMemoryDatabase is registered as Singleton so you actually share the data between DbContexts even you think you don't.
You have to create your DbContexts like this:
public abstract class UnitTestsBase
{
protected static T GetNewDbContext<T>() where T : DbContext
{
var services = new ServiceCollection();
services
.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<T>(options => options.UseInMemoryDatabase());
var serviceProvider = services.BuildServiceProvider();
var dbContext = serviceProvider.GetRequiredService<T>();
dbContext.Database.EnsureDeleted();
return dbContext;
}
}
var newTestDbContext = GetNewDbContext<TestDbContext>()
I also was led to beleive that .UseInMemoryDatabase() has no persistence, but that does not seem to be the case (at least with the latest versions)!
As noted in How can I reset an EF7 InMemory provider between unit tests? you want to do a db.Database.EnsureDeleted() BUT I also noticed that this does NOT reset auto increment ids.

NullRefernceException when calling Setup

I'm trying to do a simple Setup on mocked object, but I always get a NullRefernceException coming from the setup line:
What is the mistake that I am doing here?
Your setup is incorrect because you are confusing Moq with the .ToString() in the It.IsAny<string>().ToString().
Because of this Moq cannot generate the correct matcher and throws an exception.
It is very easy to fix this, just remove the .ToString():
[Test]
public void Test()
{
mockDatabase = new Mock<IDatabase>();
DataSet ds = new DataSet();
mockDatabase.Setup(m => m.DbQuery(It.IsAny<string>())).Returns(ds);
var sut = new BusinessClass(mockDatabase.Object);
sut.SomeMethod();
}

Compose part with specific instance

Is there any way I can compose (or get an exported value) with a specific instance as one of it's dependencies?
I have something like this:
public interface IEntityContext
{
IEntitySet<T> GetEntitySet<T>();
}
[Export(typeof(IEntitySet<MyEntity>))]
class MyEntitySet
{
public MyEntitySet(IEntityContext context)
{
}
}
// then through code
var container = ...;
using (var context = container.GetExportedValue<IEntityContext>())
{
var myEntitySet = context.GetEntitySet<MyEntity>();
// I wan't myEntitySet to have the above context constructor injected
}
I'm trying to mock something like entity framework for testability sake. Not sure though if I would want to go down this road. Anyway, should I be creating a new container for this very purpose. A container specific to the mocking of this one IEntityContext object.
So, if my understanding is correct, you want to be able to inject whatever IEntityContext is available to your instance of MyEntitySet?
[Export(typeof(IEntitySet<MyEntity>))]
public class MyEntitySet : IEntitySet<MyEntity>
{
[ImportingConstructor]
public MyEntitySet(IEntityContext context)
{
}
}
Given that you then want to mock the IEntityContext? If so, you could then do this:
var contextMock = new Mock<IEntityContext>();
var setMock = new Mock<IEntitySet<MyEntity>>();
contextMock
.Setup(m => m.GetEntitySet<MyEntity>())
.Returns(setMock.Object);
Container.ComposeExportedValue<IEntityContext>(contextMock.Object);
var context = Container.GetExportedValue<IEntityContext>();
var entitySet = context.GetEntitySet<MyEntity>();
(That's using Moq)
You can use your existing CompositionContainer infrastructure by adding an exported value.
Does that help at all? Sorry it doesn't seem exactly clear what you are trying to do...

ControllerContext.IsChildAction invocation failed with mock behavior Strict. All invocations must have a setup

I am toying around to learn how to unit test ASP.NET MVC controller actions. Specifically I'm trying to mock the ControllerContext so that I can test an action that accesses HttpContext.Current.User.Identity.Name.
I'm using Moq.
Things were going pretty well until I turned on MockBehavior.Strict. I knew that this would throw an exception if the code failed to call the thing that I marked Verifiable. Apparently, it will also throw an exception if "extra" methods where I don't provide a setup (like IsChildAction) don't get called.
[TestMethod]
public void Index_Get_AccessesUserIdentityName()
{
// Arrange
var mock = new Mock<ControllerContext>(MockBehavior.Strict);
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("treycarroll").Verifiable();
HomeController controller = new HomeController();
controller.ControllerContext = mock.Object;
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
mock.Verify();
...
}
Here is the Controller action that I'm testing:
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!"+User.Identity.Name;
return View();
}
The exception is triggered when the return View(); statement is executed. The error message tells me that I need a setup method for the call to IsChildAction so I updated my test class to this:
[TestMethod]
public void Index_Get_AccessesUserIdentityName()
{
// Arrange
var mock = new Mock<ControllerContext>(MockBehavior.Strict);
string expectedUserName = "treycarroll";
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(expectedUserName).Verifiable();
mock.SetupGet(m => m.IsChildAction).Returns(true).Verifiable();
HomeController controller = new HomeController();
controller.ControllerContext = mock.Object;
// Act
ViewResult result = controller.Index() as ViewResult;
string actualUserName = controller.ControllerContext.HttpContext.User.Identity.Name;
// Assert
mock.Verify();
Assert.AreEqual(actualUserName, expectedUserName);
Assert.IsNotNull(result);
}
...
After which I get a similar error about no setup method for ControllerContext.RouteData. By process of elimination I could wind up adding Setup methods for all the missing calls, but this doesn't seem right. Maybe I'm misunderstanding the use of MockBehavior.Strict, but I thought that you turn this on in order to avoid getting default values for your properties (such as null for the User object that I want to inspect). What am I missing here?
A strict mock will fail immediately if anything differs from the expectations. So this means, that if any method call not specified in expectation will fail. On the other hand, a non-strict mock ignore, such calls