So at the moment you just need to clone the Umbraco.CMS and use the built in tests as a guide, it's a bit of a pain but once you've got a few tests working it gets easier to know what to do.
Just for an example, I made a custom UrlProvider that I needed to test and here's a version of one of the tests I wrote using XUnit and ExpectedObjects:
[Theory]
[InlineData("/xxxx/zzzz/yyyy/")]
[InlineData("/xxxx/bbbb/cccc")]
public void GetUrl_PassedContent_ReturnsValidUrl(string parentUrl)
{
var urlProvider = new MyUrlProvider(_moqRequestSettings.Object, _moqGlobalSettings.Object, _moqLogger.Object);
var currentUri = new Uri("https://www.google.com");
var mockPropertyOne = new Mock<IPublishedProperty>();
mockPropertyOne.Setup(x => x.Alias).Returns("mockPropertyOne");
mockPropertyOne.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<string>())).Returns("Property One");
mockPropertyOne.Setup(x => x.GetSourceValue(It.IsAny<string>(), It.IsAny<string>())).Returns("Property One");
var mockPropertyTwo = new Mock<IPublishedProperty>();
mockPropertyTwo.Setup(x => x.Alias).Returns("mockPropertyTwo");
mockPropertyTwo.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<string>())).Returns("Property Two");
mockPropertyTwo.Setup(x => x.GetSourceValue(It.IsAny<string>(), It.IsAny<string>())).Returns("Property Two");
var parent = new Mock<IPublishedContent>();
parent.Setup(x => x.ContentType).Returns(new PublishedContentType(1234, "BasicContentPage", PublishedItemType.Content,
Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing));
parent.Setup(x => x.Url).Returns(parentUrl);
var content = new Mock<IPublishedContent>();
content.Setup(x => x.Parent).Returns(parent.Object);
content.Setup(x => x.ContentType).Returns(new PublishedContentType(1234, "SpecificPropertyPage", PublishedItemType.Content,
Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing));
content.Setup(x => x.GetProperty(It.Is<string>(s => s.Equals("mockPropertyOne")))).Returns(mockPropertyOne.Object);
content.Setup(x => x.GetProperty(It.Is<string>(s => s.Equals("mockPropertyTwo")))).Returns(mockPropertyTwo.Object);
var result = urlProvider.GetUrl(null, content.Object, UrlProviderMode.Auto, "en-GB", currentUri);
var expectedResult = new UrlInfo("https://www.google.com/xxxx/property-one-and-property-two", true, "en-GB").ToExpectedObject();
expectedResult.ShouldEqual(result);
}
Unfortunately I've not managed to crack the controller testing yet, the IFactory that is in UmbracoApiController (the one I'm currently using) is causing issues as it needs some setup, I'm trying to find away to add a mocked version in my spare time but not managed it yet.
So the issue I had was that when I tried to test a controller i got an error saying:
System.InvalidOperationException : No factory has been set. at Umbraco.Core.Composing.Current.get_Factory()
This was the same error you mentioned. This error is new to me since, I never got it in my Umbraco 7.x setup.
So i searched the UmbracoCms and found a way for mocking the IFactory so that it worked for me.
[SetUp]
public void SetUp() {
Current.Factory = Substitute.For<IFactory>();
}
[TearDown]
public void TearDown() {
Current.Reset();
}
(Im using NSubstitute for mocking here but I guess you can use Moq just as well.)
The teardown is ecpessially important, otherwise your first test will run fine, but the second one will trow an error saying something like "a Factory has already been set".
Hope this will help you as well, it worked for me. :)
Cheers!
I still however think it would be great if there where a "Basic testing setup" for Umbraco v8 in the documentation so that anyone interested in starting to unit test Umbraco can quickly get started.
Firstly Thanks Dennis the Factory mocking worked nicely.
Example of what I used:
public class MyControllerTests
{
private readonly Mock<IFactory> _factory;
public MyControllerTests()
{
_factory = new Mock<IFactory>();
}
[Fact]
public void MyController_Test1)
{
Current.Factory = _factory.Object;
var controller = new MyController();
var result = controller.MyMethod();
new MyResults().ToExpectedObject().ShouldEqual(result);
}
}
Secondly, agreed it would be nice if there was a Umbraco Testing Help section to the documentation.
And in the spirit of sharing, here is what I implemented.
The home controller/viewmodel implementation are based on the Route Hijacking documentation on https://our.umbraco.com/documentation/reference/routing/custom-controllers, as I just wanted to test how easy it was to Unit Test Umbraco v8 without having too much custom code implemented. That way, others could easily copy these tests.
Controller Tests:
public class HomeControllerTests : UmbracoBaseTest {
private HomeController controller;
[SetUp]
public override void SetUp() {
base.SetUp();
this.controller = new HomeController();
}
[Test]
public void GivenContentModel_WhenIndex_ThenReturnHomeViewModel() {
var content = new Mock<IPublishedContent>();
var model = new ContentModel(content.Object);
var result = (ViewResult)this.controller.Index(model);
Assert.IsAssignableFrom<HomeViewModel>(result.Model);
}
}
View Model Tests:
public class HomeViewModelTests : UmbracoBaseTest {
private Mock<IPublishedContent> content;
[SetUp]
public override void SetUp() {
base.SetUp();
this.content = new Mock<IPublishedContent>();
}
[Test]
[TestCase("", null)]
[TestCase(null, null)]
[TestCase("My Heading", "My Heading")]
[TestCase("Another Heading", "Another Heading")]
public void GivenPublishedContent_WhenGetHeading_ThenReturnPublishedContentHeadingValue(string value, string expected) {
this.SetPropertyValue(nameof(HomeViewModel.Heading), value);
var model = new HomeViewModel(this.content.Object);
Assert.AreEqual(expected, model.Heading);
}
private void SetPropertyValue(string alias, string 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(!string.IsNullOrEmpty(value));
this.content.Setup(x => x.GetProperty(alias)).Returns(property.Object);
}
}
Base Test Class:
[TestFixture]
public abstract class UmbracoBaseTest {
[SetUp]
public virtual void SetUp() {
Current.Factory = new Mock<IFactory>().Object;
}
[TearDown]
public virtual void TearDown() {
Current.Reset();
}
}
Home Controller:
public class HomeController : RenderMvcController {
public override ActionResult Index(ContentModel model) {
var viewModel = new HomeViewModel(model.Content);
return View(viewModel);
}
}
View Model:
public class HomeViewModel : ContentModel {
public HomeViewModel(IPublishedContent content) : base(content) { }
public string Heading => this.Content.Value<string>(nameof(Heading));
}
May I ask you what part of the UmbracoHelper you need to write a test for?
The UmbracoHelper bring a lot a hazzle to unit testing, since the UmbracoHelper is pretty much just a huge wrapper around a bunch of other services.
For instance, if you check the source code for the method umbracoHelper.GetDictionaryValue(string key) you will see that all it really does is return the value from the ICultureDictionary instance:
private ICultureDictionary _cultureDictionary;
/// <summary>
/// Returns the dictionary value for the key specified
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string GetDictionaryValue(string key)
{
if (_cultureDictionary == null)
{
var factory = CultureDictionaryFactoryResolver.Current.Factory;
_cultureDictionary = factory.CreateDictionary();
}
return _cultureDictionary[key];
}
So, the UmbracoHelper is great when you are in a view or something and you just want to easily fetch a dictionary value. But when it comes to unit testing, its not so great. If all you need in your controller / service is to fetch a dictionary value, why ask for a super-object that has a gazillion difference wrapped services? Why not just make your code reliable on that ONE dependency, the IDictionaryCulture with IoC and skip the UmbracoHelper completely?
Here is and example:
Here I have a controller that needs to add a dictionary value in the view model:
public class HomeController : RenderMvcController {
private readonly ICultureDictionary cultureDictionary;
public HomeController(ICultureDictionary cultureDictionary) {
this.cultureDictionary = cultureDictionary;
}
public override ActionResult Index(ContentModel model) {
var viewModel = new HomeViewModel(model.Content)
{
DictionaryValue = this.cultureDictionary["hello"]
};
return View(viewModel);
}
}
The ICultureDictionary needs to be registered in your IoC container, which is done in Umbraco 8 with Composers:
public class CultureDictionaryComposer : IUserComposer {
public void Compose(Composition composition) {
composition.Register<ICultureDictionary, DefaultCultureDictionary>(Lifetime.Scope);
}
}
Now I can write my unit tests without having to mock the huge UmbracoHelper, I just need to mock the ICultureDictionary:
[SetUp]
public void SetUp() {
Current.Factory = new Mock<IFactory>().Object;
this.cultureDictionary = new Mock<ICultureDictionary>();
this.controller = new HomeController(this.cultureDictionary.Object);
}
[Test]
[TestCase("hello", "Something", "Something")]
[TestCase("hello", "Else", "Else")]
public void GivenCultureDictionaryKey_WhenIndexAction_ThenViewModelContainsExpectedDictionaryValue(string key, string value, string expected) {
var content = new Mock<IPublishedContent>();
var model = new ContentModel(content.Object);
this.cultureDictionary.Setup(x => x[key]).Returns(value);
var result = (HomeViewModel)((ViewResult)this.controller.Index(model)).Model;
Assert.AreEqual(expected, result.DictionaryValue);
}
So basiclly this doesnt really answer your question, since you asked how to unit test the UmbracoHelper. There are examples available if you google how to unit test the UmbracoHelper, or check the source code, but they usually consist of a million lines of code for mocking every possible services that the UmbracoHelper is reliable of.
With this mentioned approach, you'll have you mock a lot less, but it required that you havent already started using the UmbracoHelper all over your code. You need to decouple your code from the UmbracoHelper in a initial setup.
Another approach.
In a few older projects, I've actually created my own IUmbracoHelper and registered a UmbracoHelperAdapter to completely decouple my code from the UmbracoHelper, which adds a little extra coding everytime you need to use a method in the helper that you havent exposed yet, but it made unit testing 10 times easier, since all my services and controllers where just dependent on a interface instead of the actual UmbracoHelper.
Hope this was any help to you, would be really interesting hearing how other tackle this issue. I love discussing unit tests. :)
Typically i wrap UmbracoHelper with my own interface and just expose the methods I am using so i can then mock those out
public interface IUmbracoHelper{
IPublishedContent GetSiteRoot{get;}
}
public class MyHelper:IUmbracoHelper {
public MyHelper(UmbracoHelper umbracoHelper){
}
}
Thanks for these useful examples!! They have helped me get to this point that might help someone further - I've got the above examples working for objects created by ModelsBuilder, populating the properties and then successfully mapping it based on this article and some previous attempts. NB all I'm testing is a simple mapping extension method ToDto() that converts my Umbraco model into my own DTO without using a third-party mapping tool.
NB usually I wrap UmbracoHelper into a wrapper object with an interface for testing, and in Umbraco 7 I setup all the context etc following Lars-Erik https://github.com/lars-erik/umbraco-unit-testing-samples. I haven't tried this yet in Umbraco 8. This example is for testing where I explicitly need access to the model properties.
The pattern I follow is as above but for models builder, using Nunit & Moq & FluentAssertions:
The objects I need are of type IPublishedElement (e.g. a nested content item) or IPublishedContent (standard content item), both consist of 0-many IPublishedProperty properties
Setup the alias, the GetValue and HasValue method for a mocked IPublishedProperty (unless it is a built in type, which you can setup directly)
Setup the content type of the mocked IPublishedElement/IPublishedContent
Setup the expected properties to be returned from the mock IPublishedElement/IPublishedContent
Create the required model object from the IPublishedElement/IPublishedContent (passed in as a parameter)
From a best practice POV I don't know if I enjoy still having to new up the model but it is the only way I can find to easily test the generated models and mock them up, so I can access the properties I need to in my tests. So, we're planning on creating a little helper to automatically do this more generically based on the type passed in. I'm also looking through the Umbraco source code as I know there are big suites of tests.
Anyway, this is the resulting code combined from above and my own stuff, for testing the simple mapping of a models builder generated EntityContent object, to a custom DTO created called ContentEntity, which contains of an Entities property consisting of 0-many Entity objects (picked in the backoffice via nested content).
public class MappingExtensionsTests
{
private EntityContent entityContent;
private Mock<IFactory> factory;
private Mock<IPublishedElement> mockIPublishedElement;
private Mock<IPublishedContent> mockEntity;
private Entity entity;
[SetUp]
public void Setup()
{
factory = new Mock<IFactory>();
Current.Factory = factory.Object;
}
[TearDown]
public void TearDown()
{
Current.Reset();
}
private void CreateSut()
{
mockIPublishedElement = new Mock<IPublishedElement>();
mockEntity = new Mock<IPublishedContent>();
var entityTextProperty = new Mock<IPublishedProperty>();
entityTextProperty.Setup(x => x.Alias).Returns("entityText");
entityTextProperty.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<string>())).Returns(new HtmlString("<b>Expected email text</b>"));
entityTextProperty.Setup(x => x.HasValue(It.IsAny<string>(), It.IsAny<string>())).Returns(!string.IsNullOrEmpty("entityText"));
var innerEntityPropertyEmailText = new Mock<IPublishedProperty>();
innerEntityPropertyEmailText.Setup(x => x.Alias).Returns("emailText");
innerEntityPropertyEmailText.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<string>())).Returns(new HtmlString("An entity's HTML string"));
innerEntityPropertyEmailText.Setup(x => x.HasValue(It.IsAny<string>(), It.IsAny<string>())).Returns(!string.IsNullOrEmpty("emailText"));
mockEntity.Setup(x => x.Id).Returns(7777);
mockEntity.Setup(x => x.Name).Returns("EntityName");
mockEntity.Setup(x => x.GetProperty(It.Is<string>(s => s.Equals("emailText")))).Returns(innerEntityPropertyEmailText.Object);
mockEntity.Setup(x => x.Properties).Returns(new List<IPublishedProperty> { innerEntityPropertyEmailText.Object });
mockEntity.Setup(x => x.ContentType).Returns(new PublishedContentType(5678, "Entity", PublishedItemType.Content,
Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing));
entity = new Entity(mockEntity.Object);
var entityProperty = new Mock<IPublishedProperty>();
entityProperty.Setup(x => x.Alias).Returns("entity");
entityProperty.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<string>())).Returns(new List<Entity>()
{
entity
});
entityProperty.Setup(x => x.HasValue(It.IsAny<string>(), It.IsAny<string>())).Returns(!string.IsNullOrEmpty("entity"));
mockIPublishedElement.Setup(x => x.Properties).Returns(new List<IPublishedProperty> { entityProperty.Object, entityTextProperty.Object });
mockIPublishedElement.Setup(x => x.ContentType).Returns(new PublishedContentType(1234, "ContentEntity", PublishedItemType.Content,
Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing));
mockIPublishedElement.Setup(x => x.GetProperty(It.Is<string>(s => s.Equals("entity")))).Returns(entityProperty.Object);
mockIPublishedElement.Setup(x => x.GetProperty(It.Is<string>(s => s.Equals("entityText")))).Returns(entityTextProperty.Object);
entityContent = new EntityContent(mockIPublishedElement.Object);
}
[Test]
public void WhenIConvertAnEntityContent_WithNullEntities_ThenItShouldReturnNull()
{
// arrange
CreateSut();
var nullEntityContent = new EntityContent(new Mock<IPublishedElement>().Object);
// act
var contentEntity = nullEntityContent.ToDto();
// assert
contentEntity.Should().Be(null);
}
[Test]
public void WhenIConvertAnEntityContent_WithEntities_ThenItShouldBecomeANewContentEntity()
{
// arrange
CreateSut();
var expected = new ContentEntity()
{
EmailText = "<b>Expected email text</b>",
Name = "EntityName",
Id = 7777
};
// act
var contentEntity = entityContent.ToDto();
// assert
contentEntity.EmailText.Should().Be(expected.EmailText);
contentEntity.Id.Should().Be(expected.Id);
contentEntity.Name.Should().Be(expected.Name);
}
}
Do you mean as a Integration Test or a Unit test? Do you want to test that something really gets created/saved etc on those events or what is it you are looking to test? Could you share some code examples?
I would like to test the saving and retrieving of content in my Umbraco project, as I have made a publish flow myself I would like to test. In this flow I copy everything from one environment to another if one of these methods is called.
Unfortunately I have not find a way to fully mock the contentService and UmbracoContextFactory if that is even the correct way to test this, let alone be possible?
[TestFixture]
public abstract class BaseTest
{
protected static IContentService _contentService;
protected static IUmbracoContextFactory _umbracoContextFactory;
[SetUp]
public virtual void SetUp()
{
Current.Factory = new Mock<IFactory>().Object;
_contentService = new Mock<IContentService>().Object;
_umbracoContextFactory = new Mock<IUmbracoContextFactory>().Object;
}
[TearDown]
public virtual void TearDown()
{
Current.Reset();
_contentService = null;
_umbracoContextFactory = null;
}
}
public class PublishFlowControllerTest : BaseTest
{
private PublishFlowController _publishFlowController;
private IContent project;
private IContent development;
private IContent staging;
private IContent production;
private IContent developmentSite;
private IContent stagingSite;
private IContent productionSite;
[SetUp]
public override void SetUp()
{
base.SetUp();
_publishFlowController = new PublishFlowController(_contentService);
project = _contentService.CreateAndSave("GDL", -1, Project.ModelTypeAlias);
development = _contentService.CreateAndSave("development", project, ContentModels.Environment.ModelTypeAlias);
staging = _contentService.CreateAndSave("staging", project, ContentModels.Environment.ModelTypeAlias);
production = _contentService.CreateAndSave("production", project, ContentModels.Environment.ModelTypeAlias);
developmentSite =_contentService.CreateAndSave("V1000", development, SiteRoot.ModelTypeAlias);
stagingSite = _contentService.CreateAndSave("V2000", staging, SiteRoot.ModelTypeAlias);
productionSite = _contentService.CreateAndSave("V3000", production, SiteRoot.ModelTypeAlias);
_contentService.SaveAndPublishBranch(project, true);
}
[Test]
public void RestoreToDevelopment()
{
Assert.IsInstanceOf(typeof(NotFoundResult), _publishFlowController.RestoreToDevelopment());
Assert.IsInstanceOf(typeof(NotFoundResult), _publishFlowController.RestoreToDevelopment(developmentSite.Id));
Assert.IsInstanceOf(typeof(OkNegotiatedContentResult<string>), _publishFlowController.RestoreToDevelopment(stagingSite.Id));
Assert.IsInstanceOf(typeof(OkNegotiatedContentResult<string>), _publishFlowController.RestoreToDevelopment(productionSite.Id));
}
[Test]
public void RestoreToStaging()
{
Assert.IsInstanceOf(typeof(NotFoundResult), _publishFlowController.RestoreToStaging());
}
[Test]
public void DeployToStaging()
{
Assert.IsInstanceOf(typeof(NotFoundResult), _publishFlowController.DeployToStaging());
}
[Test]
public void DeployToProduction()
{
Assert.IsInstanceOf(typeof(NotFoundResult), _publishFlowController.DeployToProduction());
}
}
Some ideas - you have mocked the content service, but then using .Object straight away you haven't been able set it up to return what you expect. Can you take off .Object and keep Content Service as a mock then replace the setup like this for example (I had to include the optional argument userId though):
So it is now saying what you expect the mocked Content Service to return when you call the Publish and Save methods on it - rather than calling the actual content service object itself.
e.g.
When I call create and save on the content service, it should return me an object equivalent to "project"
The System Under Test is the publish flow controller, so you do want to call the actual method on that - e.g. Restore to Development. I think if you inject the SUT creation with .Object AFTER the content service setup:
_publishFlowController = new PublishFlowController(_contentService.Object);
Things should start to work better? Hopefully that helps at all,
OK, the exception will be happening is because the IContent items need to be instantiated or mocked - at the moment they are just null, so when the assert tries to access .Id on developmentSite, it will throw a null reference exception as the item itself is null.
You are mocking the content service and telling it what content items to return, and from my understanding you aren't wanting to test the behaviour of the Content Service itself, just testing the Publish Flow Controller (whilst telling the mock content service what to return).
So you'll need to either:
Mock the IContent items, and setup for example so when you request .Id, it should return the ID you expect, e.g.
private readonly Mock<IContent> stagingMock = new Mock<IContent>();
Making sure to setup the properties you want to be returned on these, e.g. the ID to return
stagingMock.Setup(x => x.Id).Returns(123);
Or (untested) create new instances of a concrete content item, e.g
private readonly IContent development = new Content("GDL", -1, new Mock<IContentType>().Object, "");
Fill in the bits as required - this is untested code and I'd probably mock instead.
I like to structure each tests in the fashion:
//arrange (setup mocks and the system under test)
//act (call method on SUT - this is the actual unit test)
//assert (verify the behaviour that you are expecting has occurred)
You might do this anyway but as an aside - I would also split up and name each test method along the lines of what you're testing and what you're expecting as a result, e.g. GivenIAmPublishingWhenIRestoreToStagingWithNoParams_ThenANotFoundResultShouldBeReturned
Going back to the original post though, from what I can gather, you are testing that when you try and restore to the various environments using your PublishFlowController, the content service should be hit and a NotFoundResult should occur? Just to be clear in each test what behaviour we're testing and what we expect to happen.
Am I right that you have code in a flow to copy everything from one environment to another if one of these controller's methods is called, so you want to test that the content service is hit by your publish flow controller?
In that case, you would want to ensure in your Asserts section that the Content Service method should have been hit as many times as you expected.
I would split the tests into independent units, so the test with the Id passed in would be a separate test with a separate set of expectations in the Assert.
If not though, I may have misunderstood the behaviour you're trying to test! If you are trying to test the actual content service behaviour calls your controller, I have not done this before, and would mean you'd not be mocking the content service - as that would be the System Under Test.
Hope that isn't too confusing and gets you a bit further to what you're trying to achieve though.
I think the problem is when I mock the ContentService most of the attributes are null, including the UmbracoContext, . So, even if want to save the mocked content, it does not because there is nowhere to save it. Or at least that is my theory...
You are indeed right with what and the way I want to test my controller!
Unsure what the controller itself is doing behind the scenes, but good that is what we're trying to test specifically.
You have passed in a mock Content Service to the controller and told this mock what you want it to do for each method and params combo you expect it to hit.
Can you debug the test and see exactly what the values being passed into the CreateAndSave method it is throwing an exception on? As it is probably because we haven't setup the expectation on that one, so it is trying to hit a mock content service with the combination of params that we haven't told it what to do in that case.
If it doesn't come across this combination of params then the create and save setup will not be useful.
I recommend that in the "RestoreToStaging" method, add a break point and see what is happening at the point there is an exception, what variable are missing from the mocked service setups, what it is trying to call and with what params. That way you can setup on the mock.
Cracking work on the experimenting and documentation Dennis!
I ummd and ahhd about posting a reply here or creating a new thread but who better to ask than the person who wrote the docs?
I'm fairly new to unit testing so go easy on me but I'm attempting to use a fairly simple Umbraco project as a bit of a test bed. I'm currently stuck on trying to test a composer which registers a dependency and having a tough time figuring out how to test it.
The code will probably speak volumes so without further ado, here's the composer I'd like to test. As you can see, it simply registers a service coded against an interface.
using Papermoon.Umbraco.Utils.Services;
using Papermoon.Umbraco.Utils.Services.Interfaces;
using Umbraco.Core;
using Umbraco.Core.Composing;
namespace Papermoon.Umbraco.Aldus.Core.Composers
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ServicesComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Register<IPapermoonContentTypeContainerService, PapermoonContentTypeContainerService>();
}
}
}
After a lot of playing around, I found some code in the Umbraco source which means I can get a test passing based on the idea of registering a type. However, that is in no way in context of the ServicesComposer class. Hence, that wouldn't count against my code coverage and actually testing the class, rather than the ability to register something. Here's the code anyway:
using System;
using Moq;
using NUnit.Framework;
using Papermoon.Umbraco.Aldus.Core.Composers;
using Papermoon.Umbraco.Utils.Services;
using Papermoon.Umbraco.Utils.Services.Interfaces;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Logging;
namespace Papermoon.Umbraco.Aldus.Core.Tests.Composers
{
[TestFixture]
public class ServicesComposerTests
{
private ServicesComposer _servicesComposer;
[SetUp]
public void SetUp()
{
_servicesComposer = new ServicesComposer();
}
[Test]
public void Compose_WhenCalled_RegistersContentTypeContainerService()
{
Func<IFactory, IFactory> factoryFactory = null;
var mockedRegister = Mock.Of<IRegister>();
var mockedFactory = Mock.Of<IFactory>();
// the mocked register creates the mocked factory
Mock.Get(mockedRegister)
.Setup(x => x.CreateFactory())
.Returns(mockedFactory);
// the mocked register can register a factory factory
Mock.Get(mockedRegister)
.Setup(x => x.Register(It.IsAny<Func<IFactory, IFactory>>(), Lifetime.Singleton))
.Callback<Func<IFactory, IFactory>, Lifetime>((ff, lt) => factoryFactory = ff);
// the mocked factory can invoke the factory factory
Mock.Get(mockedFactory)
.Setup(x => x.GetInstance(typeof(IPapermoonContentTypeContainerService)))
.Returns(() => new Mock<IPapermoonContentTypeContainerService>().Object);
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), "", logger);
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>());
var factory = composition.CreateFactory();
var resolved = factory.GetInstance<IPapermoonContentTypeContainerService>();
Assert.IsNotNull(resolved);
}
}
}
And the below code shows where I am at the moment, and is probably close to what the test should look like (if a little messy currently). I'm potentially way off the mark here so any help would go down a storm!
using System;
using Moq;
using NUnit.Framework;
using Papermoon.Umbraco.Aldus.Core.Composers;
using Papermoon.Umbraco.Utils.Services;
using Papermoon.Umbraco.Utils.Services.Interfaces;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Logging;
namespace Papermoon.Umbraco.Aldus.Core.Tests.Composers
{
[TestFixture]
public class ServicesComposerTests
{
private ServicesComposer _servicesComposer;
[SetUp]
public void SetUp()
{
_servicesComposer = new ServicesComposer();
Current.Factory = new Mock<IFactory>().Object;
}
[Test]
public void Compose_WhenCalled_RegistersContentTypeContainerService()
{
var mockedRegister = Mock.Of<IRegister>();
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), "", logger);
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>());
_servicesComposer.Compose(composition);
var resolved = Current.Factory.GetInstance<IPapermoonContentTypeContainerService>();
Assert.IsNotNull(resolved);
}
}
}
Unit Testing v8
Hi. Are there any documentation of how to properly mock dependencies for unit testing Umbraco v8?
For instance: mocking the dependencies needed for a RenderMvcController or a SurfaceController in v8?
I don't know of any documentation, but you could checkout the umbraco until tests
I am also very interested in finding a way to do this.
Anyone?
I added a request for the Umbraco Documentation on how to get started with Unit Testing in Umbraco 8: https://github.com/umbraco/UmbracoDocs/issues/1627
So at the moment you just need to clone the Umbraco.CMS and use the built in tests as a guide, it's a bit of a pain but once you've got a few tests working it gets easier to know what to do.
Just for an example, I made a custom UrlProvider that I needed to test and here's a version of one of the tests I wrote using XUnit and ExpectedObjects:
Hope this helps get you started.
Thanks Simon Andrews
Thanks Simon. And how does your controller tests look like? Could you show some examples of those?
Hi Dennis,
Unfortunately I've not managed to crack the controller testing yet, the IFactory that is in UmbracoApiController (the one I'm currently using) is causing issues as it needs some setup, I'm trying to find away to add a mocked version in my spare time but not managed it yet.
Same issue here, I figured it out however.
So the issue I had was that when I tried to test a controller i got an error saying:
This was the same error you mentioned. This error is new to me since, I never got it in my Umbraco 7.x setup. So i searched the UmbracoCms and found a way for mocking the IFactory so that it worked for me.
(Im using NSubstitute for mocking here but I guess you can use Moq just as well.)
The teardown is ecpessially important, otherwise your first test will run fine, but the second one will trow an error saying something like "a Factory has already been set".
Hope this will help you as well, it worked for me. :) Cheers!
I still however think it would be great if there where a "Basic testing setup" for Umbraco v8 in the documentation so that anyone interested in starting to unit test Umbraco can quickly get started.
Firstly Thanks Dennis the Factory mocking worked nicely.
Example of what I used:
Secondly, agreed it would be nice if there was a Umbraco Testing Help section to the documentation.
And in the spirit of sharing, here is what I implemented.
The home controller/viewmodel implementation are based on the Route Hijacking documentation on https://our.umbraco.com/documentation/reference/routing/custom-controllers, as I just wanted to test how easy it was to Unit Test Umbraco v8 without having too much custom code implemented. That way, others could easily copy these tests.
Controller Tests:
View Model Tests:
Base Test Class:
Home Controller:
View Model:
Hopefully it could be helpful to someone.
All the best, Dennis
Hi, this is great. Thanks for sharing.
How would you go about testing a function that relies on UmbracoHelper ?
Than you
Im not sure yet in v8, but I’m working on it. :)
I’ll let you know when I have it!
Hi Alexander.
May I ask you what part of the UmbracoHelper you need to write a test for?
The UmbracoHelper bring a lot a hazzle to unit testing, since the UmbracoHelper is pretty much just a huge wrapper around a bunch of other services.
For instance, if you check the source code for the method
umbracoHelper.GetDictionaryValue(string key)
you will see that all it really does is return the value from the ICultureDictionary instance:(There is a great blog post about unit tests and Umbraco helper here: https://glcheetham.name/2017/01/29/mocking-umbracohelper-using-dependency-injection-the-right-way/)
So, the UmbracoHelper is great when you are in a view or something and you just want to easily fetch a dictionary value. But when it comes to unit testing, its not so great. If all you need in your controller / service is to fetch a dictionary value, why ask for a super-object that has a gazillion difference wrapped services? Why not just make your code reliable on that ONE dependency, the IDictionaryCulture with IoC and skip the UmbracoHelper completely?
Here is and example:
Here I have a controller that needs to add a dictionary value in the view model:
The ICultureDictionary needs to be registered in your IoC container, which is done in Umbraco 8 with Composers:
Now I can write my unit tests without having to mock the huge UmbracoHelper, I just need to mock the ICultureDictionary:
So basiclly this doesnt really answer your question, since you asked how to unit test the UmbracoHelper. There are examples available if you google how to unit test the UmbracoHelper, or check the source code, but they usually consist of a million lines of code for mocking every possible services that the UmbracoHelper is reliable of.
With this mentioned approach, you'll have you mock a lot less, but it required that you havent already started using the UmbracoHelper all over your code. You need to decouple your code from the UmbracoHelper in a initial setup.
Another approach. In a few older projects, I've actually created my own IUmbracoHelper and registered a UmbracoHelperAdapter to completely decouple my code from the UmbracoHelper, which adds a little extra coding everytime you need to use a method in the helper that you havent exposed yet, but it made unit testing 10 times easier, since all my services and controllers where just dependent on a interface instead of the actual UmbracoHelper.
Hope this was any help to you, would be really interesting hearing how other tackle this issue. I love discussing unit tests. :)
Wow that is very enlightning and well explained. It seems to work very well. Thanks.
Thank you! I’m glad you liked it!
Cheers!
Alexandre,
Typically i wrap UmbracoHelper with my own interface and just expose the methods I am using so i can then mock those out
Something along those lines.
Writing unit tests would be a lot easier if the Umbraco class constructors were not all Internal for the core objects.
Thanks for these useful examples!! They have helped me get to this point that might help someone further - I've got the above examples working for objects created by ModelsBuilder, populating the properties and then successfully mapping it based on this article and some previous attempts. NB all I'm testing is a simple mapping extension method ToDto() that converts my Umbraco model into my own DTO without using a third-party mapping tool.
NB usually I wrap UmbracoHelper into a wrapper object with an interface for testing, and in Umbraco 7 I setup all the context etc following Lars-Erik https://github.com/lars-erik/umbraco-unit-testing-samples. I haven't tried this yet in Umbraco 8. This example is for testing where I explicitly need access to the model properties.
The pattern I follow is as above but for models builder, using Nunit & Moq & FluentAssertions:
From a best practice POV I don't know if I enjoy still having to new up the model but it is the only way I can find to easily test the generated models and mock them up, so I can access the properties I need to in my tests. So, we're planning on creating a little helper to automatically do this more generically based on the type passed in. I'm also looking through the Umbraco source code as I know there are big suites of tests.
Anyway, this is the resulting code combined from above and my own stuff, for testing the simple mapping of a models builder generated EntityContent object, to a custom DTO created called ContentEntity, which contains of an Entities property consisting of 0-many Entity objects (picked in the backoffice via nested content).
Updated to make a bit more generic:
Thank you for sharing Emma! #h5yr
Is there a way to test the contentService methods, for example CreateAndSave() and SaveAndPublish().
I have mocked a IContentService myself but everything in this mocked service is null and I cannot find a way to fix this yet...
Thanks!
Do you mean as a Integration Test or a Unit test? Do you want to test that something really gets created/saved etc on those events or what is it you are looking to test? Could you share some code examples?
I would like to test the saving and retrieving of content in my Umbraco project, as I have made a publish flow myself I would like to test. In this flow I copy everything from one environment to another if one of these methods is called.
Unfortunately I have not find a way to fully mock the contentService and UmbracoContextFactory if that is even the correct way to test this, let alone be possible?
Hi Davy,
Some ideas - you have mocked the content service, but then using .Object straight away you haven't been able set it up to return what you expect. Can you take off .Object and keep Content Service as a mock then replace the setup like this for example (I had to include the optional argument userId though):
So it is now saying what you expect the mocked Content Service to return when you call the Publish and Save methods on it - rather than calling the actual content service object itself.
e.g.
The System Under Test is the publish flow controller, so you do want to call the actual method on that - e.g. Restore to Development. I think if you inject the SUT creation with .Object AFTER the content service setup:
Things should start to work better? Hopefully that helps at all,
Emma
I implemented the Mocks as you said, but I still have the same issue :/
After the Setups et cetera the variables that need to be returned are null.
When I run my test the second Assert will state: Message: System.NullReferenceException : Object reference not set to an instance of an object.
Hi Davy
OK, the exception will be happening is because the IContent items need to be instantiated or mocked - at the moment they are just null, so when the assert tries to access
.Id
on developmentSite, it will throw a null reference exception as the item itself is null.You are mocking the content service and telling it what content items to return, and from my understanding you aren't wanting to test the behaviour of the Content Service itself, just testing the Publish Flow Controller (whilst telling the mock content service what to return).
So you'll need to either:
Mock the IContent items, and setup for example so when you request .Id, it should return the ID you expect, e.g.
Making sure to setup the properties you want to be returned on these, e.g. the ID to return
Or (untested) create new instances of a concrete content item, e.g
Fill in the bits as required - this is untested code and I'd probably mock instead.
I like to structure each tests in the fashion:
You might do this anyway but as an aside - I would also split up and name each test method along the lines of what you're testing and what you're expecting as a result, e.g. GivenIAmPublishingWhenIRestoreToStagingWithNoParams_ThenANotFoundResultShouldBeReturned
Going back to the original post though, from what I can gather, you are testing that when you try and restore to the various environments using your PublishFlowController, the content service should be hit and a NotFoundResult should occur? Just to be clear in each test what behaviour we're testing and what we expect to happen.
Am I right that you have code in a flow to copy everything from one environment to another if one of these controller's methods is called, so you want to test that the content service is hit by your publish flow controller?
In that case, you would want to ensure in your Asserts section that the Content Service method should have been hit as many times as you expected.
I would split the tests into independent units, so the test with the Id passed in would be a separate test with a separate set of expectations in the Assert.
If not though, I may have misunderstood the behaviour you're trying to test! If you are trying to test the actual content service behaviour calls your controller, I have not done this before, and would mean you'd not be mocking the content service - as that would be the System Under Test.
Hope that isn't too confusing and gets you a bit further to what you're trying to achieve though.
Thanks
Emma
Hi,
I think the problem is when I mock the ContentService most of the attributes are null, including the UmbracoContext, . So, even if want to save the mocked content, it does not because there is nowhere to save it. Or at least that is my theory...
You are indeed right with what and the way I want to test my controller!
Hi
Unsure what the controller itself is doing behind the scenes, but good that is what we're trying to test specifically.
You have passed in a mock Content Service to the controller and told this mock what you want it to do for each method and params combo you expect it to hit.
Can you debug the test and see exactly what the values being passed into the CreateAndSave method it is throwing an exception on? As it is probably because we haven't setup the expectation on that one, so it is trying to hit a mock content service with the combination of params that we haven't told it what to do in that case.
e.g.:
If it doesn't come across this combination of params then the create and save setup will not be useful.
I recommend that in the "RestoreToStaging" method, add a break point and see what is happening at the point there is an exception, what variable are missing from the mocked service setups, what it is trying to call and with what params. That way you can setup on the mock.
Emma
FYI: I've packaged a few of these examples into a PR that just got merged to the Umbraco Documentation: https://our.umbraco.com/documentation/Implementation/Unit-Testing
Hello Dennis is there any way to verify the content model that uses an interface IHtmlString or IEnumerable
Thank you so much for you help with the documentation of Unit Test.
Hi Ernesto. Do you mean like aN RTE property on a ContentModel and a list of children for example (IEnumerable
I can look at an example tomorrow morning. Glad you liked the documentation!
Yes, I'm trying to understand how to focus the unit test for this case and a Repeatable String is IEnumerable < String >
Hi again Ernesto, sorry for the delay.
Is this maybe what you had i mind?
Based on this content:
Hi Dennis, thank you so much for your help, that solution is perfect to my problem. Sorry, I'm new with Unit Testing
Glad I could help! Have a great day! 👍🏻
Cracking work on the experimenting and documentation Dennis!
I ummd and ahhd about posting a reply here or creating a new thread but who better to ask than the person who wrote the docs?
I'm fairly new to unit testing so go easy on me but I'm attempting to use a fairly simple Umbraco project as a bit of a test bed. I'm currently stuck on trying to test a composer which registers a dependency and having a tough time figuring out how to test it.
The code will probably speak volumes so without further ado, here's the composer I'd like to test. As you can see, it simply registers a service coded against an interface.
After a lot of playing around, I found some code in the Umbraco source which means I can get a test passing based on the idea of registering a type. However, that is in no way in context of the
ServicesComposer
class. Hence, that wouldn't count against my code coverage and actually testing the class, rather than the ability to register something. Here's the code anyway:And the below code shows where I am at the moment, and is probably close to what the test should look like (if a little messy currently). I'm potentially way off the mark here so any help would go down a storm!
Thanks!
Ben
The tests here and some more have been refactored and placed here: https://our.umbraco.com/documentation/Implementation/Unit-Testing/
is working on a reply...