I am trying to implement unit testing for a surface controller. The GET method tries to read some properties of the CurrentPage and POST method tries to redirect to the ChildPage of the CurrentPage. The problem I am encountering is I can never get the UmbracoContext in my unit tests. Has someone encountered and solved this? Any help much appreciated
This is an example of what my code will do on POST
public ActionResult PostRegistrationForm(ChosenRegistrationViewModel model, HttpPostedFileBase photo)
{
//do some logic and if successful redirect
var thankYouPage = CurrentPage.ChildrenOfType(ChosenThankYouPage.ModelTypeAlias).FirstOrDefault();
return this.RedirectToUmbracoPage(thankYouPage, "eventId=" + model.SignUpEventId.ToString() + "®istrationId=" + model.Id.ToString());
}
public class TestingTests : UmbracoBaseTest
{
private readonly UmbracoHelper umbracoHelper;
private readonly Mock<IPublishedContent> currentPage;
private readonly RegistrationController controller;
public TestingTests()
{
base.SetUp();
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.MembershipHelperImplementation);
this.controller = new RegistrationController(Mock.Of<IUmbracoContextAccessor>(), Mock.Of<IUmbracoDatabaseFactory>(), base.ServiceContext, AppCaches.NoCache, Mock.Of<ILogger>(), Mock.Of<IProfilingLogger>(), umbracoHelper);
}
[Fact]
public void Given_SuccessfullRegistration_When_PostRegistrationForm_Then_RedirectToThankYouPageWithExpectedQueryString()
{
var thankYouPageType = new Mock<IPublishedContentType>();
thankYouPageType.Setup(x => x.Alias).Returns("thankYouPage");
var thankYouPage = new Mock<IPublishedContent>();
thankYouPage.Setup(x => x.Id).Returns(123);
thankYouPage.Setup(x => x.Url).Returns("/thankyou");
thankYouPage.Setup(x => x.ContentType).Returns(thankYouPageType.Object);
this.currentPage.Setup(x => x.ChildrenForAllCultures).Returns(new List<IPublishedContent>() { thankYouPage.Object });
var registrationModel = new ChosenRegistrationViewModel()
{
Id = 456,
SignUpEventId = 789
};
var result = (RedirectResult)this.controller.PostRegistrationForm(registrationModel);
result.Url.ShouldBe("/thankyou?eventId=789®istrationId=456");
}
}
public class RegistrationController : SurfaceController
{
private readonly UmbracoHelper umbracoHelper;
public RegistrationController(IUmbracoContextAccessor umbracoContextAccessor,
IUmbracoDatabaseFactory umbracoDatabaseFactory, ServiceContext serviceContext, AppCaches appCaches,
ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper) : base(
umbracoContextAccessor, umbracoDatabaseFactory, serviceContext, appCaches, logger, profilingLogger,
umbracoHelper)
{
this.umbracoHelper = umbracoHelper;
}
[HttpPost]
public ActionResult PostRegistrationForm(ChosenRegistrationViewModel model)
{
//do some logic and if successful redirect
var thankYouPage = this.umbracoHelper.AssignedContentItem.ChildrenOfType("thankYouPage").FirstOrDefault();
return Redirect(string.Format($"{thankYouPage?.Url}?eventId={model.SignUpEventId}®istrationId={model.Id}"));
}
}
public class ChosenRegistrationViewModel
{
public int Id { get; set; }
public int SignUpEventId { get; set; }
}
I ended up ditching RedirectToUmbracoPageResult because it turned out to be quite hard to Assert (queryStringValues is a private field, Url fields uses static resolver Current to resolve current page instead of DI), instead I used plain old return Redirect(url); Hope this works for you.
I am writing test code for Umbraco 10 and I was struggling with this exact same issue and I stumbled across this thread. The base test class seemed to be Umbraco 8.
Did anyone have an answer for the more recent Umbraco versions?
Hi Isaac.
You are correct, the the base class mentioned in this thread does not exist any more in v9+ since there was very little plumbing needed to perform testing in v9+ we chose to skip the base class and instead I lace the little plumbing in each implementation.
What is it specifically that you are struggling with, maybe I can help?
I was struggling to get the value of CurrentPage to be mocked correctly when testing the following function in my SurfaceController:
I was able to finally get it to work by mocking and setting up the content Features in a way that UmbracoRouteValues would be set. It seems very messy and there is probably a better solution but it's at least at a point where that gets mocked.
Yes the reason that everything becomes messy is because you are using CurrentPage it it is a bit complicated when working with unit testing.
I usually try to avoid CurrentPage and instead inject the currentPage through my Action like this:
Then it becomes a lot less complicated to mock and inject this from my unit tests, like this:
content in this case is just a mocked IPublishedContent interface.
So this can probably reduce some code in your project.
Regarding the Value getters I have a slight memory that the extension method .Value has an underlying dependency that was a bit messy to mock (cant remember which one sorry you have to dig in to the Core).
Unit Testing Surface Controllers
Hi,
I am trying to implement unit testing for a surface controller. The GET method tries to read some properties of the CurrentPage and POST method tries to redirect to the ChildPage of the CurrentPage. The problem I am encountering is I can never get the UmbracoContext in my unit tests. Has someone encountered and solved this? Any help much appreciated
This is an example of what my code will do on POST
Hi Poornima.
Instead of using
CurrentPage
, use theUmbracoHelper.AssignedContentItem
. Easier to mock.Instructions on how tro mock the UmbracoHelper is found here: https://our.umbraco.com/documentation/Implementation/Unit-Testing/
Cheers!
Thanks Dennis, will give it a shot..
Hi Poornima. Based on your example code (and our DM conversation on LinkedIn) it created an example of how a test for your example could look like.
The base class
UmbracoBaseTest
is the same base class I mention on this page: https://our.umbraco.com/documentation/Implementation/Unit-Testing/I ended up ditching
RedirectToUmbracoPageResult
because it turned out to be quite hard to Assert (queryStringValues is a private field, Url fields uses static resolver Current to resolve current page instead of DI), instead I used plain oldreturn Redirect(url);
Hope this works for you.Have a great day and stay safe!
Thanks a lot Dennis. Much much appreciated :-)
Hi there,
I am writing test code for Umbraco 10 and I was struggling with this exact same issue and I stumbled across this thread. The base test class seemed to be Umbraco 8.
Did anyone have an answer for the more recent Umbraco versions?
Hi Isaac. You are correct, the the base class mentioned in this thread does not exist any more in v9+ since there was very little plumbing needed to perform testing in v9+ we chose to skip the base class and instead I lace the little plumbing in each implementation.
What is it specifically that you are struggling with, maybe I can help?
I was struggling to get the value of CurrentPage to be mocked correctly when testing the following function in my SurfaceController:
I was able to finally get it to work by mocking and setting up the content Features in a way that UmbracoRouteValues would be set. It seems very messy and there is probably a better solution but it's at least at a point where that gets mocked.
Now I am struggling to get CurrentPage.Value
I am trying to use the following helper function:
which I found here: https://adolfi.dev/blog/mocking-property-values-umbraco/
If you have any insight on getting Properties and their Value getters setup, that would be very helpful!
Thanks, Isaac
Yes the reason that everything becomes messy is because you are using CurrentPage it it is a bit complicated when working with unit testing.
I usually try to avoid CurrentPage and instead inject the currentPage through my Action like this:
Then it becomes a lot less complicated to mock and inject this from my unit tests, like this:
content in this case is just a mocked IPublishedContent interface.
So this can probably reduce some code in your project.
Regarding the Value getters I have a slight memory that the extension method .Value has an underlying dependency that was a bit messy to mock (cant remember which one sorry you have to dig in to the Core).
There is however a easier way to avoid this. Instead of using the extension method you can use the GetValue() method on the Property like I am doing here: https://github.com/Adolfi/Umbraco-v10-Unit-Testing/blob/main/Umbraco10Testing.Tests/Features/PageViewModel.cs#L10C89-L10C89
That should work with the mocking you have screenshoted.
Also if you didn't already find it, I have an example project in GitHub with a bunch of Umbraco 9-10 unit tests: https://github.com/Adolfi/Umbraco-v10-Unit-Testing
Hopefully it might be useful as inspiration for you.
Cheers!
is working on a reply...