I'm trying to set up a unit test for a renderMVCController and a SurfaceController. When I try to access the CurrentPage property on the surfacecontroller, I get an error saying that there is no routecontext.
I have then tried to set my UmbracoContextAccessor like so:
public abstract class UmbracoBaseTest : BaseWebTest
{
public ServiceContext ServiceContext;
public MembershipHelper MembershipHelper;
public UmbracoHelper UmbracoHelper;
public UmbracoMapper UmbracoMapper;
public IUmbracoContextAccessor UmbracoContextAccessor;
public Mock<ICultureDictionary> CultureDictionary;
public Mock<ICultureDictionaryFactory> CultureDictionaryFactory;
public Mock<IPublishedContentQuery> PublishedContentQuery;
public Mock<HttpContextBase> HttpContext;
public Mock<IMemberService> memberService;
public Mock<IPublishedMemberCache> memberCache;
[SetUp]
public virtual void SetUp()
{
this.SetupHttpContext();
this.SetupCultureDictionaries();
this.SetupPublishedContentQuerying();
this.SetupMembership();
var settings = SettingsForTests.GenerateMockUmbracoSettings();
var globalSettings = SettingsForTests.GenerateMockUmbracoSettings();
this.ServiceContext = ServiceContext.CreatePartial();
this.UmbracoHelper = new UmbracoHelper(Mock.Of<IPublishedContent>(), Mock.Of<ITagQuery>(), this.CultureDictionaryFactory.Object, Mock.Of<IUmbracoComponentRenderer>(), this.PublishedContentQuery.Object, this.MembershipHelper);
this.UmbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new List<IMapDefinition>()));
var umbracoContextAccessor = new Mock<IUmbracoContextAccessor>();
var umbracoContext = GetUmbracoContext("http://localhost", -1, setSingleton: true);
umbracoContextAccessor.Setup(t => t.UmbracoContext)
.Returns(umbracoContext);
this.UmbracoContextAccessor = umbracoContextAccessor.Object;
}
But this does not seem to work. I get an error:
Umbraco.Core.Exceptions.PanicException : Could not resolve the running test fixture from type name RedCarnation.Web.Tests.Controllers.MasterControllerTests.
To use base classes from Umbraco.Tests, add your test assembly to TestOptionAttributeBase.ScanAssemblies
Ok, so I think I figured it out. Passing in the ContentModel into the Controller Index action will serve just like the CurrentPage.ID, and is testable. I still have much to learn.
Hello Gabor,
How did you manage to get around this?
My controller actions don't take in parameters they are usually empty and CurrentPage is being filled in automatically.
When I pass in it asks for me to either generate a new action that takes in ContentModel or add contentmodel to my existing action.
Could you share any insight you might have gained from this? I have been trying to add unit testing to an umbraco project it's been quite a journey just to get the tests to fail up until now
How my controller action looks:
public ActionResult IndexNew()
{
ViewBag.Dictionary = GetIndexDictionary();
var home = CurrentPage.Root();
var heroimage = CurrentPage.Value<IPublishedContent>("heroImage");
ViewBag.heroImageUrl = heroimage != null ? heroimage.GetCropUrl(1920, 1080) : "/assets/images/placeholders/Article-Placeholder.jpg";
var market = Translate.MARKET(ViewBag.Dictionary["languageCodeDict"]);
DDCApiModel api = new DDCApiModel();
ViewBag.CountResult = api.CountAllProducts(market);
return PartialView(string.Format("{0}IndexNew.cshtml", ViewPath), CurrentPage);
}
You have to separate things out a bit to get unit testing to work. For example - CurrentPage Requires an UmbracoContext, which I haven't managed to mock.
Changing it to:
public ActionResult IndexNew(ContentModel currentPage)
{
ViewBag.Dictionary = GetIndexDictionary();
var home = currentPage.Root();
var heroimage = CurrentPage.Value<IPublishedContent>("heroImage");
ViewBag.heroImageUrl = heroimage != null ? heroimage.GetCropUrl(1920, 1080) : "/assets/images/placeholders/Article-Placeholder.jpg";
var market = Translate.MARKET(ViewBag.Dictionary["languageCodeDict"]);
DDCApiModel api = new DDCApiModel();
ViewBag.CountResult = api.CountAllProducts(market);
return PartialView(string.Format("{0}IndexNew.cshtml", ViewPath), CurrentPage);
}
Will be the same as using the CurrentPage property, except this way you can mock your contentModel that you will be passing in:
var result = (BrandHomePageModel)((ViewResult)this.controller.Index(new ContentModel(Mock.Of<IPublishedContent>()))).Model;
Understood,
How do you call the function then because it now requires a parameter so we would need to pass in ContentModel when trying to render the page in the frontend
I mean, we use Vue.JS, and we simply route to /PageController/Index and that being a RenderMVCController will work for a page with alias page. we then return json to vuew that renders the FE.
What front end do you use? If it's simple MVC, then just route to the index method, and Umbraco build the contentModel for you.
What is GetUmbracoContext ?
Is this why my test is throw "Factory not set"?
What I've tried in Test Class
private ProductsController controller;
private readonly Mock<IPublishedContent> currentPage;
private Mock<IFactory> Factory;
public ProductControllerTests()
{
base.SetUp();
Factory = new Mock<IFactory>();
this.currentPage = new Mock<IPublishedContent>();
this.umbracoHelper = new UmbracoHelper(this.currentPage.Object, Mock.Of<ITagQuery>(), this.CultureDictionaryFactory.Object, Mock.Of<IUmbracoComponentRenderer>(), this.PublishedContentQuery.Object, this.MembershipHelper);
this.controller = new ProductsController(Mock.Of<IUmbracoContextAccessor>(), Mock.Of<IUmbracoDatabaseFactory>(), base.ServiceContext, AppCaches.NoCache, Mock.Of<Umbraco.Core.Logging.ILogger>(), Mock.Of<Umbraco.Core.Logging.IProfilingLogger>(), base.umbracoHelper);
}
[Test]
public void WhenIndexAction_ThenResultIsIsAssignableFromContentResult()
{
var productsPage = new Mock<IPublishedContent>();
var model = new Umbraco.Web.Models.ContentModel(new Mock<IPublishedContent>().Object);
var result = this.controller.IndexMain(model.Content) as ViewResult;
var productPage = (Products)result.ViewData.Model;
Assert.AreEqual(productPage.Name, "Products");
}
This is so you can pass in some parameters when testing the controller.
This is my controller test class
public class BrandStandardPageControllerTests : UmbracoBaseTest
{
private Bp02BrandStandardController controller;
private Mock<IBrandStandardPageRepository> _repository;
[SetUp]
public override void SetUp()
{
base.SetUp();
_repository = new Mock<IBrandStandardPageRepository>();
_repository.Setup(t => t.GetPage(It.IsAny<object>())).Returns(new PageModel());
this.controller = new Bp02BrandStandardController(_repository.Object,
UmbracoContextAccessor, Mock.Of<IGlobalSettings>(), base.ServiceContext, AppCaches.NoCache, Mock.Of<IProfilingLogger>(), base.UmbracoHelper);
}
[Test]
public void GivenContentModel_WhenControllerIsCalled_BrandStandardPageIsReturned()
{
var result = (PageModel)((ViewResult)this.controller.Index(new ContentModel(Mock.Of<IPublishedContent>()))).Model;
Assert.IsNotNull(result);
}
}
And my base class setup is:
[SetUp]
public virtual void SetUp()
{
this.SetupHttpContext();
this.SetupCultureDictionaries();
this.SetupPublishedContentQuerying();
this.SetupMembership();
} public virtual void SetupHttpContext()
{
this.HttpContext = new Mock<HttpContextBase>();
}
public virtual void SetupCultureDictionaries()
{
this.CultureDictionary = new Mock<ICultureDictionary>();
this.CultureDictionaryFactory = new Mock<ICultureDictionaryFactory>();
this.CultureDictionaryFactory.Setup(x => x.CreateDictionary()).Returns(this.CultureDictionary.Object);
}
public virtual void SetupPublishedContentQuerying()
{
this.PublishedContentQuery = new Mock<IPublishedContentQuery>();
}
public virtual void SetupMembership()
{
this.memberService = new Mock<IMemberService>();
var memberTypeService = Mock.Of<IMemberTypeService>();
var membershipProvider = new MembersMembershipProvider(memberService.Object, memberTypeService);
this.memberCache = new Mock<IPublishedMemberCache>();
this.MembershipHelper = new MembershipHelper(this.HttpContext.Object, this.memberCache.Object, membershipProvider, Mock.Of<RoleProvider>(), memberService.Object, memberTypeService, Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.NoCache, Mock.Of<ILogger>());
}
public static void SetupPropertyValue(Mock<IPublishedContent> publishedContentMock, string alias, object value, string culture = null, string segment = null)
{
var property = new Mock<IPublishedProperty>();
property.Setup(x => x.Alias).Returns(alias);
property.Setup(x => x.GetValue(culture, segment)).Returns(value);
property.Setup(x => x.HasValue(culture, segment)).Returns(value != null);
publishedContentMock.Setup(x => x.GetProperty(alias)).Returns(property.Object);
}
public static void SetupPropertyValue(Mock<IPublishedElement> publishedContentMock, string alias, object value, string culture = null, string segment = null)
{
var property = new Mock<IPublishedProperty>();
property.Setup(x => x.Alias).Returns(alias);
property.Setup(x => x.GetValue(culture, segment)).Returns(value);
property.Setup(x => x.HasValue(culture, segment)).Returns(value != null);
publishedContentMock.Setup(x => x.GetProperty(alias)).Returns(property.Object);
}
At its most basic this is what you need to run a test. When you have a no factory set, then it means that something is trying to access the umbracoContext. Do away with any code that tries to do this. Abstract it out, mock it away, in order to get it to work.
How to test Controllers with CurrentPage in V8
Hi,
I'm trying to set up a unit test for a renderMVCController and a SurfaceController. When I try to access the CurrentPage property on the surfacecontroller, I get an error saying that there is no routecontext.
I have then tried to set my UmbracoContextAccessor like so:
But this does not seem to work. I get an error:
Any idea how I can get this working?
Thanks,Gabor
Can anyone help out with the above? Is there any documentation on how to test controllers where we are using the CurrentPage property?
Ok, so I think I figured it out. Passing in the ContentModel into the Controller Index action will serve just like the CurrentPage.ID, and is testable. I still have much to learn.
Hello Gabor, How did you manage to get around this? My controller actions don't take in parameters they are usually empty and CurrentPage is being filled in automatically. When I pass in it asks for me to either generate a new action that takes in ContentModel or add contentmodel to my existing action. Could you share any insight you might have gained from this? I have been trying to add unit testing to an umbraco project it's been quite a journey just to get the tests to fail up until now
How my controller action looks:
Hi Armend,
You have to separate things out a bit to get unit testing to work. For example - CurrentPage Requires an UmbracoContext, which I haven't managed to mock.
Changing it to:
Will be the same as using the CurrentPage property, except this way you can mock your contentModel that you will be passing in:
It's then up to you to test the logic.
Hop this makes sense Gabor
Understood, How do you call the function then because it now requires a parameter so we would need to pass in ContentModel when trying to render the page in the frontend
What front-end do you use?
I mean, we use Vue.JS, and we simply route to /PageController/Index and that being a RenderMVCController will work for a page with alias page. we then return json to vuew that renders the FE.
What front end do you use? If it's simple MVC, then just route to the index method, and Umbraco build the contentModel for you.
We just use simple MVC. I understood that and managed to get it working but my tests still don't run throwing factory not set.
I did take UmbracoBaseTest from umbraco documentation but it seems yours has some extra code which is:
What is GetUmbracoContext ? Is this why my test is throw "Factory not set"? What I've tried in Test Class
Controller:
Btw installing Umbraco.Tests crashes our Umbraco installation and umbraco fails to boot Umbraco.Core.Exceptions.BootFailedException: Boot failed.
I don't think I managed to get Umbraco.Tests working either...
So my controller constructor is:
This is so you can pass in some parameters when testing the controller.
This is my controller test class
And my base class setup is:
At its most basic this is what you need to run a test. When you have a no factory set, then it means that something is trying to access the umbracoContext. Do away with any code that tries to do this. Abstract it out, mock it away, in order to get it to work.
is working on a reply...