1

我对单元测试还很陌生,所以请放轻松!我正在尝试使用一个相当简单的 Umbraco 8 项目作为测试平台。我目前一直在尝试测试注册依赖项的作曲家,并且很难弄清楚如何测试它。

代码可能会说话,所以不用多说,这是我想测试的作曲家。如您所见,它只是注册了一个针对接口编码的服务:

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>();
        }
    }
}

在玩了很多之后,我在 Umbraco 源代码中找到了一些代码,这意味着我可以根据注册类型的想法获得测试通过。但是,这绝不是在ServicesComposer课程的背景下。因此,这不会影响我的代码覆盖率和实际测试类,而不是注册某些东西的能力。无论如何,这是代码:

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);
        }
    }
}

下面的代码显示了我目前的位置,并且可能接近测试的样子(如果目前有点混乱)。我可能在这里偏离了目标,所以任何帮助都会风靡一时!

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);
        }
    }
}

我还尝试模拟合成,看看我是否可以验证该Register方法已运行,但由于这是一个静态方法,我收到以下错误:

Extension methods (here: RegisterExtensions.Register) may not be used in setup / verification expressions.

这是让我遇到该错误的代码:

[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);

    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(IFactory)))
        .Returns(() => factoryFactory?.Invoke(mockedFactory));

    var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
    var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), "", logger);
    var composition = new Mock<Composition>(mockedRegister, typeLoader, logger, new Mock<IRuntimeState>().Object);

    composition.Verify(c => c.Register<IPapermoonContentTypeContainerService, PapermoonContentTypeContainerService>(It.IsAny<Lifetime>()));
}

最终,我失败了(可能我的目标太高了!),我不能 100% 确定要在这里测试什么。我的想法是我想测试运行IPapermoonContentTypeContainerService后是否可解析,_serviceComposer.Compose即它不是 null 以确保它已注册到容器中。在这一点上这可能是不可能的,我想知道composition.Register<IPapermoonContentTypeContainerService, PapermoonContentTypeContainerService>();被调用的测试是否足够(因为实际的注册部分是第三方的,因此不被测试)。或者,我是不是找错了树,实际上根本不应该对其进行测试?

谢谢!

4

1 回答 1

1

Register<TService, TImplementing>是一种静态扩展方法。您不能模拟扩展方法,您需要查看它的源代码并查看它在后台调用的方法。

例如,假设我有一个ILoggerwhich 暴露ILogger.Write(info level, string message)然后我有一个扩展方法:

public static void Info(this ILoggerlogger, string message) => logger.Write("Info", message);

Info在 的模拟实例上调用时ILogger,仍会调用扩展方法并调用模拟ILogger.Write对象。

正如您从源代码中看到的那样,通用扩展正在调用另一个重载——这是您需要设置/验证的重载:

composition.Verify(c => c.Register(typeof(IPapermoonContentTypeContainerService), typeof(PapermoonContentTypeContainerService), It.IsAny<Lifetime>()));

我不熟悉服务作曲家并怀疑这是不可能的;但不是Compose(Composition composition),使用IRegisterComposition继承自)将允许您composition直接模拟而不必模拟它的依赖项......

于 2019-08-22T10:44:04.180 回答