【问题标题】:Unit Testing Umbraco 8 Composers单元测试 Umbraco 8 Composers
【发布时间】:2019-08-21 12:39:32
【问题描述】:

我对单元测试还很陌生,所以请放轻松!我正在尝试使用一个相当简单的 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);
        }
    }
}

我还尝试模拟 Composition 以查看是否可以验证 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&lt;IPapermoonContentTypeContainerService, PapermoonContentTypeContainerService&gt;(); 是否被调用就足够了(因为实际的注册部分是第三方的,因此不需要测试)。或者,我是不是找错了树,实际上根本不应该对其进行测试?

谢谢!

【问题讨论】:

  • imo umbraco 团队应该更改 the contract for IComposer 以使用 IRegister 参数或像 IComposition 这样的新合约来定义 Compose。在这一点上,尝试测试您的 DI 注册似乎比它的价值更麻烦

标签: c# unit-testing dependency-injection umbraco umbraco8


【解决方案1】:

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

例如,假设我有一个ILogger,它暴露了ILogger.Write(info level, string message),那么我有一个扩展方法:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-04
    • 1970-01-01
    • 2021-11-16
    • 2019-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多