random .NET and web development musings

Following a recent embarrassing episode where I had been banging on about how unit testing saves the world, then released a broken site, I decided a solution to prevent this happening again was necessary!

I currently test my MVC ViewResults like this:

public class when_requesting_a_login_form : ControllerTest<LoginController>
{
	private ViewResult result;

	protected override void Act()
	{
		this.result = this.SubjectUnderTest.LogIn() as ViewResult;
	}

	[Test]
	public void the_login_view_should_be_returned()
	{
		this.result.ViewName.ShouldEqual("Login");
	}
}

However this doesn’t check that the Login.aspx view actually exists (which it didn’t in the case I’m talking about, as I only found out at runtime). Below is my new test:

[Test]
public void the_login_view_should_be_returned()
{
	AssertViewIsCorrectAndExists(result, "Login");
}

This assertion not only checks that the view names match, but also that the file exists on disc.

The solution is far from elegant, but it works, which is better than nothing!

Before I show you the codes, some prerequisite knowledge about my solution setup is required.

My folders are ALWAYS organised like this:

/src/Project.Web/
/src/Project.Domain/
/src/Project.Tests.Unit/

with these folder names matching the assembly names. This allows the following very fragile code to work:

protected void AssertViewIsCorrectAndExists(ViewResult result, string viewName)
{
	ViewEngines.Engines.Clear();
	ViewEngines.Engines.Add(new TestingViewEngine());
	Assert.AreEqual(viewName, result.ViewName, "View names do not match");

	var controllerContext = Stub<ControllerContext>();
	controllerContext.RouteData = new RouteData();
	controllerContext.Controller = SubjectUnderTest;

	var controllerName = typeof(TController).Name;
	controllerContext.RouteData.Values.Add("controller", controllerName.Substring(0, controllerName.Length - "controller".Length));

	var viewEngineResult = result.ViewEngineCollection.FindView(controllerContext, result.ViewName, result.MasterName);
	Assert.IsNotNull(viewEngineResult.View, "View '" + result.ViewName + "' could not be found");
}

public class TestingViewEngine : WebFormViewEngine
{
	public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
	{
		// hack of death!!!!
		var webProjectPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, Path.Combine("../../../", controllerContext.Controller.GetType().Assembly.GetName().Name)));

		foreach(var location in this.ViewLocationFormats)
		{
			var virtualViewPath = string.Format(location, viewName, controllerContext.RouteData.Values["controller"]);

			// crudely trim leading ~/
			virtualViewPath = virtualViewPath.Substring(2);

			var fullPath = Path.Combine(webProjectPath, virtualViewPath);

			if (File.Exists(fullPath))
				return new ViewEngineResult(MockRepository.GenerateStub<IView>(), this);
		}

		return new ViewEngineResult(this.ViewLocationFormats);
	}
}

Crude I know, but I hope this helps someone!

NO COMMENTS
Post a comment