Thanks for getting back on this. Much appreciated.
I think it would also be a good idea to have a section on the MVC documentation on http://our.umbraco.org/documentation/reference/mvc/ regarding unit test.
Also, it might be a good idea to have a screencast on http://beta.umbraco.tv/videos/developer/fundamentals/ and show how we could test umbraco controllers.
Cheers,
Jorge
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on [this zip file][2].
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
[1]: https://github.com/umbraco/Umbraco-CMS
[2]: http://jlusar.es/get/umbraco%20mvc%20test/UmbracoTest.zip
Simply put I don’t want to use your test base classes. I see that as a fundamental barrier to me being able to do any unit testing against SurfaceControllers or any of the other Umbraco-specific controller types.
The reason I don’t want to use them is I don’t trust them in unit test scope. When writing a unit test I don’t want to rely on something I don’t have any control over, setting up dependencies that I have no visibility on, immediately my tests are no longer testing my code but instead running code that might impact the outcome without my knowledge.
Looking at the dependency chain described in the other posts here it suggests inheriting from BaseRoutingTest which in turn inherits from BaseWebTest which inherits BaseDatabaseFactoryTest and that finally inherits BaseUmbracoApplicationTest.
Digging through these classes there are a number of things that are immediately going to have me noping out immediately such as:
· Generating a folder structure
· Loading plugins into the app domain
· Creating a database (!!)
Simply put even before my test starts up hundreds of lines of code have been executed that I don’t have any control over and that I didn’t request.
For the record the controller action I wanted to test was communicating to an external service in our system and then rendering a view. In that view we then use come Umbraco helpers to insert content and a child macro, hence the reason I’m down the SurfaceController path.
My solution
Now that I’ve voiced my opinion on why the current solution doesn’t work it seems only fair to add my own solution, that would be to expose an interface for the UmbracoContext, like IUmbracoContext. This could be returned via the UmbracoContext.Current member so that the current codebase doesn’t really need to change, really nothing would be that different, it just means that instead of using a class (which is virtually impossible to stub out due to internal constructors) you have an interface instead which you can work with, stub out and control.
If/When you write integration tests then the Umbraco test classes would make sense but when you’re doing a unit test on controller actions that don’t need Umbraco features you should be able to work without them.
Aaron Powell
IE MVP | IE userAgent
http://www.aaron-powell.com | http://twitter.com/slace |
Skype: aaron.l.powell | Github |
BitBucket
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to
umbra...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/umbraco-dev/4f7a5eb0-16b7-4668-92f5-37463c679fd5%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Now I still think an interface is a viable idea, if we look at the public instance members of UmbracoContext (in 6.1.5) there are:
· Application
· Security
· ContentCache
· MediaCache
· IsFrontEndUmbracoRequest
· PublishedContentRequest
· HttpContext
· IsDebug
· PageId
· UmbracoUser
· InPreviewMode
Now if we discount what isn’t an Umbraco type we’re left with:
· Application
· Security
· ContentCache
· MediaCache
· PublishedContentRequest
· UmbracoUser (although I discount this because I’m pretty sure you could mock that)
And well yes the dependency chain expands out from there. But if at the very least UmbracoContext was an interface that you could assign null to all those you’re making Controllers infinitely more testable, it’s not until you’re wanting to consume Umbraco from a controller action (which to me is a design smell to begin with, you should do that in the View) that you start hitting roadblocks.
To illustrate my point, you can’t unit test this controller:
public class MySurfaceController : SurfaceController {
[ChildActionOnly]
public ActionResult Index() {
return View(new MyModel());
}
}
Like this:
[TestClass]
public class MySurfaceControllerTests {
[TestMethod]
public void IndexReturnsViewResult() {
var controller = new MySurfaceController();
var result = controller.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
Without taking dependencies on the file system, a database and executing several hundred lines of code. If I could pass null or a stub of UmbracoContext (or IUmbracoContext) then my test can pass.
Yes I’m aware this is a slightly contrived example, but it’s not far off what I was actually wanting to do.
Aaron Powell
IE MVP | IE userAgent
http://www.aaron-powell.com | http://twitter.com/slace |
Skype: aaron.l.powell | Github |
BitBucket
From: umbra...@googlegroups.com [mailto:umbra...@googlegroups.com]
On Behalf Of Shannon Deminick
Sent: Wednesday, 2 October 2013 10:27 AM
To: umbra...@googlegroups.com
Subject: Re: Unit testing with Umbraco
Yes Aaron, we love your opinions :P ... but we also already know that this all needs work (as already mentioned).
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to
umbra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/umbraco-dev/5f9ed0be-a316-434e-ab59-507828e93afd%40googlegroups.com.
If it’s the former then that’s something someone with less time restrictions can undertake.
Aaron Powell
IE MVP | IE userAgent
http://www.aaron-powell.com | http://twitter.com/slace |
Skype: aaron.l.powell | Github |
BitBucket
From: umbra...@googlegroups.com [mailto:umbra...@googlegroups.com]
On Behalf Of Shannon Deminick
Sent: Wednesday, 2 October 2013 11:16 AM
To: umbra...@googlegroups.com
Subject: Re: Unit testing with Umbraco
There's was a discussion someplace else about this and passing in null - i thought it was this thread but apparently not. Of course the absolute easiest way to let you do what you want currently is to remove the null check for UmbracoContext - you can submit a PR if you want for that but that's not going to solve your issue right now unless you want to work on a custom build and in that case you can just change what you like in your project anyways.
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to
umbra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/umbraco-dev/2aec3228-f3dc-4bb8-97fb-1c999ebdbdd5%40googlegroups.com.
I just have to toss in a couple of cents for when times aren’t as tight.
Context is that I can’t not unit-test this controller/model stuff I have any longer, it’s gotten too complex.
Guess what? I’m in a bee-hive of internal/mocking hell. (Yes, I tried the hacks and base classes, but they don’t do)
Anyway, I’m gonna get through it no matter, and I don’t really care whether you’re gonna break it in the future. I’ll adapt.
But I really really have to comment on one thing you said below:
> “One fundamental problem with changing to an interface is that the UmbracoContext exposes lots of internal bits, so inevitably in the core we'll be casting this to the real instance to get at those internal parts”
The whole point of using interfaces and layering your software into modules and interchangeable parts is that you DON’T want to expose and / or cast stuff to conrete types.
Get over it! You’re doing it wrong!
Sorry for the harsh message, but fact of the matter is that that is the only way to build maintainable software, and the only way we can trust the API and our own software built on top of it.
Haven’t looked at U7 much yet, but I really hope you’ve either read or will read:
- “Working effectively with legacy code” http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
- and “Aglie software development” http://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445/ref=pd_sim_b_5
before adding ANY new class with either internal or private declaration and/or ctor; and which doesn’t inherit from a public interface, and is ONLY used as such.
I’d much rather change my code when you break backwards compatibility.
OK, point made, frustration vented and tips provided.
Sorry again for being a bit frustrated just now.
Looking forward to better APIs. J
Lars-Erik
From: umbra...@googlegroups.com [mailto:umbra...@googlegroups.com]
On Behalf Of Shannon Deminick
Sent: 2. oktober 2013 07:17
To: umbra...@googlegroups.com
Subject: Re: Unit testing with Umbraco
Time is extremely tight right now, as much as I'd love to jump in there and get something nicer working for you. One fundamental problem with changing to an interface is that the UmbracoContext exposes lots of internal bits, so inevitably in the core we'll be casting this to the real instance to get at those internal parts which also means that if ( "when " - because people will) start trying to use an interface that isn't the object we're looking for in their unit tests when they try to do stuff, they will get errors and will wonder why.
The next problem is all of the public things UmbracoContext exposes, we would inevitably have to make interfaces of a few of those too like ApplicationContext, etc... this is all a ton of work and yet we still don't want to expose all of the underlying stuff in there publicly so we're back at this option -> For us to just give you a simple test helper class to create these instances for you to use - this is an easier and better option for us.
So in the interim until we have time to sort this out you can use the base classes - if you don't want to inherit than you can use the hack I've mentioned above.
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to umbra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/umbraco-dev/24aa1d38-7b74-40e7-a651-5bffb1fadf9b%40googlegroups.com.
Say no more...
The entire code below had to be written to achieve the three lines in bold/red near the top of the code.
(You can clearly see where I started to get tired of “clean code” J )
The extension method under test is actually rather new, but I hope this code serves to show that there is still something horribly wrong in some of the architecture going into the system.
using System;
using System.Collections.Generic;
using System.Reflection;
using NUnit.Framework;
using Rhino.Mocks;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Web;
namespace Umbraco.Web.Tests
{
[TestFixture]
public class ApplicationContextMockingTests
{
private static IDatabaseUnitOfWorkProvider databaseUnitOfWorkProvider;
private static RepositoryFactory repositoryFactory;
private static IContentService contentService;
private static IMediaService mediaService;
private static IUnitOfWorkProvider unitOfWorkProvider;
private static BasePublishingStrategy basePublishingStrategy;
private const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
private const BindingFlags PublicStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy;
[SetUp]
public void SetUp()
{
databaseUnitOfWorkProvider = MockRepository.GenerateStub<IDatabaseUnitOfWorkProvider>();
repositoryFactory = MockRepository.GenerateStub<RepositoryFactory>();
unitOfWorkProvider = MockRepository.GenerateStub<IUnitOfWorkProvider>();
basePublishingStrategy = MockRepository.GenerateStub<BasePublishingStrategy>();
contentService = MockRepository.GenerateStub<IContentService>();
mediaService = MockRepository.GenerateStub<IMediaService>();
}
[Test]
public void WowIReallyHaveToDoAllThisToUseSimpleTypeConvertersWithAMockedInterfacesExtensions()
{
CanCreateFullPackage();
var appBase = MockRepository.GenerateStub<UmbracoApplicationBase>();
var bootMgr = new PleaseJustAPartialBootManager(appBase);
bootMgr.InitializeResolvers();
bootMgr.FreezeResolution();
SqlSyntaxContext.SqlSyntaxProvider = new SqlServerSyntaxProvider();
var uow = MockRepository.GenerateStub<IDatabaseUnitOfWork>();
unitOfWorkProvider.Stub(p => p.GetUnitOfWork()).Return(uow);
var repo = MockRepository.GenerateStub<IContentTypeRepository>();
repositoryFactory.Stub(f => f.CreateContentTypeRepository(Arg<IDatabaseUnitOfWork>.Is.Anything)).Return(repo);
var contentType = new ContentType(-1);
contentType.AddPropertyGroup("");
contentType.AddPropertyType(
new PropertyType(
new DataTypeDefinition(-1, new Guid("9910AA59-FF84-4F37-A61C-51D295499C83"))
)
{
Alias = "a"
}
);
repo.Stub(r => r.GetByQuery(Arg<IQuery<IContentType>>.Is.Anything))
.Return(new[] { contentType });
var content = MockRepository.GenerateStub<IPublishedContent>();
content.Stub(c => c.DocumentTypeAlias).Return("ohJustADocType");
var prop = MockRepository.GenerateStub<IPublishedContentProperty>();
prop.Stub(p => p.Value).Return("a");
content.Stub(c => c.GetProperty("a")).Return(prop);
var value = content.GetPropertyValue<string[]>("a");
Assert.IsNotNull(value);
Assert.AreEqual(1, value.Length);
}
[Test]
public void CanCreateApplicationContext()
{
var context = CreateApplicationContext();
Assert.IsNotNull(context);
}
[Test]
public void CanCreateServiceContext()
{
var context = CreateServiceContext();
Assert.IsNotNull(context);
}
[Test]
public void CanCreateContentTypeService()
{
var service = CreateContentTypeService();
Assert.IsNotNull(service);
}
[Test]
public void CanSetContentTypeServiceOnServiceContext()
{
var context = CreateServiceContext();
var contentTypeService = CreateContentTypeService();
SetService(context, "_contentTypeService", contentTypeService);
Assert.AreSame(contentTypeService, context.ContentTypeService);
}
[Test]
public void CanSetServiceContextOnApplicationService()
{
var applicationContext = CreateApplicationContext();
var serviceContext = CreateServiceContext();
SetFieldValue(applicationContext, "_services", serviceContext);
Assert.AreSame(serviceContext, applicationContext.Services);
}
[Test]
public void CanCreateFullPackage()
{
var applicationContext = CreateApplicationContext();
var serviceContext = CreateServiceContext();
var contentTypeService = CreateContentTypeService();
var dataTypeService = CreateDataTypeService();
SetService(serviceContext, "_contentTypeService", contentTypeService);
SetService(serviceContext, "_dataTypeService", dataTypeService);
SetFieldValue(applicationContext, "_services", serviceContext);
SetStaticProperty<ApplicationContext>("Current", applicationContext);
Assert.AreSame(applicationContext, ApplicationContext.Current);
}
[Test]
public void CanCreateMappingResolver()
{
var type = Type.GetType("Umbraco.Core.Persistence.Mappers.MappingResolver, Umbraco.Core");
if (type == null)
throw new Exception("Couldn't create MappingResolver type");
var ctor = type.GetConstructor(new[] {typeof (Func<IEnumerable<Type>>)});
if (ctor == null)
throw new Exception("Didn't find MappingResolver ctor");
var resolver = ctor.Invoke(new object[] { new Func<IEnumerable<Type>>(() => Type.EmptyTypes) });
SetStaticProperty(type, "Current", resolver);
Assert.IsNotNull(resolver);
Assert.AreSame(resolver, type.GetProperty("Current", PublicStatic).GetValue(null));
}
private void SetStaticProperty<T>(string propertyName, object value)
{
var type = typeof (T);
SetStaticProperty(type, propertyName, value);
}
private static void SetStaticProperty(Type type, string propertyName, object value)
{
var prop = type.GetProperty(propertyName, PublicStatic);
if (prop == null)
throw new Exception("Didn't find property " + propertyName);
prop.SetValue(null, value);
}
private static ContentTypeService CreateContentTypeService()
{
var service = new ContentTypeService(databaseUnitOfWorkProvider, repositoryFactory, contentService, mediaService);
return service;
}
private static DataTypeService CreateDataTypeService()
{
var service = new DataTypeService(databaseUnitOfWorkProvider, repositoryFactory);
return service;
}
private static ApplicationContext CreateApplicationContext()
{
var type = typeof (ApplicationContext);
var ctor = type.GetConstructor(PrivateInstance, null, new[] {typeof (bool)}, null);
if (ctor == null)
throw new Exception("Couldn't find ctor with bool parameter");
var context = (ApplicationContext) ctor.Invoke(new object[] {false});
return context;
}
private static ServiceContext CreateServiceContext()
{
var type = typeof (ServiceContext);
var ctor = type.GetConstructor(PrivateInstance, null,
new[] {typeof (IDatabaseUnitOfWorkProvider), typeof (IUnitOfWorkProvider), typeof (BasePublishingStrategy)}, null);
if (ctor == null)
throw new Exception("Couldn't find ctor with misc parameters");
var context = (ServiceContext) ctor.Invoke(new object[] {databaseUnitOfWorkProvider, unitOfWorkProvider, basePublishingStrategy});
return context;
}
private static void SetService<T>(ServiceContext serviceContext, string fieldName, T contentTypeService)
{
var value = new Lazy<T>(() => contentTypeService);
SetFieldValue(serviceContext, fieldName, value);
}
private static void SetFieldValue(object owner, string fieldName, object value)
{
var field = owner.GetType().GetField(fieldName, PrivateInstance);
if (field == null)
throw new Exception("Didn't find field " + fieldName);
field.SetValue(owner, value);
}
}
public class PleaseJustAPartialBootManager : CoreBootManager
{
public PleaseJustAPartialBootManager(UmbracoApplicationBase umbracoApplication) : base(umbracoApplication)
{
}
public new void InitializeResolvers()
{
base.InitializeResolvers();
}
public new void FreezeResolution()
{
base.FreezeResolution();
}
}
public class StringArrayConverter : IPropertyEditorValueConverter
{
public bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias)
{
return propertyEditorId == new Guid("9910AA59-FF84-4F37-A61C-51D295499C83");
}
public Attempt<object> ConvertPropertyValue(object value)
{
var str = value as string;
var array = new string[0];
if (str != null)
array = str.Split(new[] {";"}, StringSplitOptions.RemoveEmptyEntries);
return new Attempt<object>(true, array);
}
}
}
Just to sum up,
The previously posted code can be shrunk to 10% or so with the following changes to the newly implemented(?) core stuff:
- Make a public empty ApplicationContext ctor
- Make ApplicationContext.Current settable
- Make the ServiceContext setter public
- Change the relevant Lazy<T> in ServiceContext to use the service interfaces and add a setting method
- Or drop the lazy usage of Lazy and make them regular references by using the age old lazy pattern, and make the properties settable
-
Oh, and while I’m at it – make it a whole lot easier to stub usage of IUnitOfWork and IRepository<T>.
The tight coupling with Query<T>.Builder and MappingResolver f***s everything up.
I’m 100% sure nobody’s going to f*** with those members outside a test fixture.
If they do, they’ll delete the code and don’t do it again.
Keep up the good work! J
G’night.
L-E
Glad to hear you’re on top of it. J
I’d be happy to contribute, but I guess architectural changes is a bit cumbersome through PRs.
Tagging tasks on issues.umbraco.org as “up for grabs” for the community would be nice, not sure if anything like that is already done?
L-E
From: umbra...@googlegroups.com [mailto:umbra...@googlegroups.com]
On Behalf Of Shannon Deminick
Sent: 9. oktober 2013 07:23
To: umbra...@googlegroups.com
Subject: Re: Unit testing with Umbraco
I managed to make some progress on this today in both the 6.2. and 7.0 branches btw.
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to umbra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/umbraco-dev/e49a2198-aa90-4239-8d40-c9239def58db%40googlegroups.com.
On a sidenote, I took my own advice and adapted, so now I’ve adapted (as in the pattern) my way away from the entire core. J
(Of course using my code generator project with a couple of custom generator implementations – worth checking out todays commits on https://github.com/lars-erik/Umbraco.CodeGen. ;) )
So no rush after all.. keep doing good stuff to 6.2/7.
L-E
--
You received this message because you are subscribed to the Google Groups "Umbraco development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
umbraco-dev...@googlegroups.com.
To post to this group, send email to umbra...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/umbraco-dev/0163c1427322433cb4dd6b62068ab169%40DBXPR04MB078.eurprd04.prod.outlook.com.
For more options, visit https://groups.google.com/groups/opt_out.
private BlogPostSurfaceController GetController()
{
// Create contexts via test base class methods
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
// We need to add a value to the controller's RouteData, otherwise calls to CurrentPage
// (or RedirectToCurrentUmbracoPage) will fail
// Unfortunately some types and constructors necessary to do this are marked as internal
// Create instance of RouteDefinition class using reflection
// - note: have to use LoadFrom not LoadFile here to type can be cast (http://stackoverflow.com/questions/3032549/c-on-casting-to-the-same-class-that-came-from-another-assembly
var assembly = Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "umbraco.dll"));
var reflectedRouteDefinitionType = assembly.GetType("Umbraco.Web.Mvc.RouteDefinition");
var routeDefinition = Activator.CreateInstance(reflectedRouteDefinitionType);
// Similarly create instance of PublishedContentRequest
// - note: have to do this a little differently as in this case the class is public but the constructor is internal
var reflectedPublishedContentRequestType = assembly.GetType("Umbraco.Web.Routing.PublishedContentRequest");
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var culture = CultureInfo.InvariantCulture;
var publishedContentRequest = Activator.CreateInstance(reflectedPublishedContentRequestType, flags, null, new object[] { new Uri("/test", UriKind.Relative), routingContext }, culture);
// Set properties on reflected types (not all of them, just the ones that are needed for the test to run)
var publishedContentRequestPublishedContentProperty = reflectedPublishedContentRequestType.GetProperty("PublishedContent");
publishedContentRequestPublishedContentProperty.SetValue(publishedContentRequest, MockIPublishedContent(), null);
var publishedContentRequestProperty = reflectedRouteDefinitionType.GetProperty("PublishedContentRequest");
publishedContentRequestProperty.SetValue(routeDefinition, publishedContentRequest, null);
// Then add it to the route data tht will be passed to the controller context
// - without it SurfaceController.CurrentPage will throw an exception of: "Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"
var routeData = new RouteData();
routeData.DataTokens.Add("umbraco-route-def", routeDefinition);
// Create the controller with the appropriate contexts
var controller = new BlogPostSurfaceController(umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, routeData, controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}