Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Simon 687 posts 1061 karma points
    Nov 02, 2016 @ 15:05
    Simon
    0

    Unit Testing - Moq mocking for Umbraco Models Builder Object

    Hi Guys,

    Does anyone know how I can mock, using moq, an umbraco models builder object?

    I have searched about it and they say that properties should be vritual in order to be able to use SetUpGet and they are not virual and there are no setters.

    I would like to mock my umbraco models builder object in order to be able to compare.

    Thank you for your help.

    Kind Regards

  • Simon 687 posts 1061 karma points
    Nov 02, 2016 @ 20:29
    Simon
    0

    Any idea guys?

    Thank you

  • Tim Watts 90 posts 392 karma points
    Jan 31, 2017 @ 08:31
    Tim Watts
    0

    Did you get anywhere with this Simon? I'm having the same issue now.

    ModelsBuilder uses extension methods, which can't be used with Moq.

    Whilst this works:

    var value = content.GetPropertyValue("myAlias");
    

    This doesn't:

    var value = content.GetPropertyValue<string>("myAlias");
    

    And unfortunately it's the second method which is used by ModelsBuilder.

    Apologies I haven't got a solution to add, but I'm hoping you've already found one which you can share!

    Tim

  • Mark Cassar 5 posts 80 karma points
    Mar 21, 2017 @ 15:59
    Mark Cassar
    5

    We have found a solution by using the following class. In case it helps anybody. See below for example of code:

    /// <summary>
    /// Use this class to Mock any item which is built by the Umbraco Models Builder.  This is required
    /// because by default the Models Builder item calls several extension methods of the IPublishedProperty & IPublishedContent.
    /// This makes life way easy.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ModelsBuilderMock<T> where T : IPublishedContent
    {
        private Mock<IPublishedContent> _mockedContent;
        public T Mock { get; }
    
        public Mock<IPublishedContent> MockedContent { get; }
    
        public ModelsBuilderMock()
        {
            _mockedContent = new Mock<IPublishedContent>();
            Mock = CreateObjectWithAnyConstructor<T>(_mockedContent.Object);
        }
    
        /// <summary>
        /// Mock a property and its value.  
        /// </summary>
        /// <param name="propertyExpression"></param>
        /// <param name="value"></param>
        public void SetProperty(Expression<Func<T, object>> propertyExpression, object value)
        {
            string propertyName = ExpressionHelper.GetExpressionText(propertyExpression);
            //The Models Builder actual umbraco Alias are in the formate of, for e.g. 'metaRobots', or 'age'.
            propertyName = TextHelpers.SetFirstLetterLowercase(propertyName);
    
            var mockedProperty = new Mock<IPublishedProperty>();
            //This is because the extension method GetPropertyValue from the PublishedContentExtensions
            _mockedContent.Setup(x => x.GetProperty(propertyName, false)).Returns(mockedProperty.Object);
            mockedProperty.SetupAllProperties();
            mockedProperty.SetupGet(x => x.Value).Returns(value);
            mockedProperty.SetupGet(x => x.HasValue).Returns(true);
    
    
        }
    
        //Ideally place these static methods in a separate file
        public static T CreateObjectWithAnyConstructor<T>(params object[] parameters)
        {
            return (T)CreateObjectWithAnyConstructor(typeof(T), parameters);
    
        }
        public static object CreateObjectWithAnyConstructor(Type typeToCreate, params object[] parameters)
        {
            ConstructorInfo constructor = null;
    
            try
            {
                // Binding flags exclude public constructors.
                constructor =
                    typeToCreate.GetConstructors()
                        .Where(x => x.GetParameters().Length == parameters.Length)
                        .FirstOrDefault();
                //constructor = typeToCreate.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
            }
            catch (Exception)
            {
                throw;
            }
    
            if (constructor == null || constructor.IsAssembly)
                // Also exclude internal constructors.
                throw new InvalidOperationException(string.Format("A private or " +
                        "protected constructor is missing for '{0}'.", typeToCreate.Name));
            object _instance = null;
            try
            {
                _instance = constructor.Invoke(parameters);
            }
            catch (MemberAccessException ex)
            {
                var m = Regex.Match(ex.Message, "Cannot create an instance of (.*?)Factory because it is an abstract class.");
                if (m.Success)
                {
                    string factoryName = m.Groups[1].Value + "Factory";
                    string msg = ex.Message +
                                 " - If you are using this factory, make sure you mark it as 'UsedInProject' in the builder";
                    throw new InvalidOperationException(msg);
                }
                else
                {
                    throw;
                }
    
    
            }
            catch (Exception)
            {
                throw;
            }
            return _instance;
        }
    }
    

    This is the example of how to use it then. FakeModelsBuilderData is a Models Builder generated class.

            var basePageMock = new ModelsBuilderMock<FakeModelsBuilderData>();
            var galleryValue = "aaaaaaaaa";
            basePageMock.SetProperty(x => x.Gallery, galleryValue);
            basePageMock.SetProperty(x => x.MetaRobots, "bbbbb");
            var metaRobotsValue = "ccc";
            basePageMock.SetProperty(x => x.MetaRobots, metaRobotsValue);
            Assert.AreEqual(basePageMock.Mock.Gallery, galleryValue);
            Assert.AreEqual(basePageMock.Mock.MetaRobots, metaRobotsValue);
    

    Hope this helps! :)

  • Rahul Patel 6 posts 76 karma points
    Aug 25, 2017 @ 21:40
    Rahul Patel
    0

    This was very helpful, thanks so much!

  • Sotiris Filippidis 242 posts 1190 karma points
    Aug 16, 2018 @ 20:54
    Sotiris Filippidis
    0

    This was indeed an extremely helpful piece of code. The only issue I had was that it wouldn't set the DocumentTypeAlias property so tests would fail if it was needed in code. After some digging, I added this line of code in the constructor:

    _mockedContent.Setup(x => x.DocumentTypeAlias).Returns(typeof(T).UnderlyingSystemType.GetField("ModelTypeAlias", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).GetValue(null).ToString());
    

    And now my mocks come with the document type alias correctly set. I'm pretty sure that the bindingflags are redundant in the above code, I've just left them for completeness. This is a trick to get values from a type's constants using reflection, which I just learned about :) Hope it's useful.

  • Sotiris Filippidis 242 posts 1190 karma points
    Aug 16, 2018 @ 23:06
    Sotiris Filippidis
    0

    One more thing I discovered was that ExpressionHelper sometimes returned an empty string instead of the property's name. After searching a bit, I found that this is expected, and somebody put together some code to fix that. So here it is:

      public static string GetExpressionText<TModel>(Expression<Func<TModel, object>> expression)
        {
            var expr = (LambdaExpression)expression;
            if (expr.Body.NodeType == ExpressionType.Convert)
            {
                var ue = expr.Body as UnaryExpression;
                var me = ((ue != null) ? ue.Operand : null) as MemberExpression;
                return me.Member.Name;
            }
            return ExpressionHelper.GetExpressionText(expr);
        }
    

    I use this as a static method inside the ModelsBuilderMock class, and swapped the following line in the SetProperty method:

    string propertyName = ExpressionHelper.GetExpressionText(propertyExpression);
    

    with this:

    string propertyName = GetExpressionText(propertyExpression);
    

    Code taken from https://gist.github.com/rmariuzzo/4716555

  • Hassan Mourad 2 posts 72 karma points
    Oct 15, 2018 @ 16:23
    Hassan Mourad
    0

    i found this solution pretty awesome , and i am using it. but i have a problem , i cant seem able to set properties like "id - sortorder - name" , or in general any system property

    do u have any idea why ? or how to accomplish this ?

  • Julien Kulker 29 posts 191 karma points
    1 week ago
    Julien Kulker
    0

    Same problem

  • javafun 14 posts 47 karma points c-trib
    18 days ago
    javafun
    0

    I'm getting compilation error and it seems SetFirstLetterLowercase method doesn't seem available on TestHelper class.

    Can someone point me out where to find this method?

    Thanks, V

  • javafun 14 posts 47 karma points c-trib
    17 days ago
    javafun
    0

    Any hints would be very appreciated!

Please Sign in or register to post replies

Write your reply to:

Draft