设置模拟对象 属性 然后验证它是否在方法调用中使用

Set a Mock Object Property Then Verify It Is Used in Method Call

我正在尝试创建一个测试来验证是否调用了特定方法。该方法接受两个参数,我想检查是否使用具有特定 属性 集的对象调用它。

代码如下:

private void InitialiseContentTypes()
{
    IContentType blogContentTypeComposition = _blogContentTypeFactory.GetComposition();

    _umbracoContentTypeService.Save(blogContentTypeComposition);
}

这从工厂获取组合,然后在 Save 方法中使用它。在测试中,blogContentTypeComposition 应该有一个名为 属性 的别名设置为 aldusBlogComposition (blogContentTypeComposition.Alias).

测试代码如下:

[Test]
[TestCase("aldusBlogComposition")]
public void Initialise_WhenCalled_SavesComposition(string alias)
{
    Mock<IContentType> contentType = new Mock<IContentType>();

    _blogContentTypeFactory
        .Setup(f => f.GetComposition())
        .Callback(() => { contentType.SetupProperty(ct => ct.Alias, alias); });

    _component.Initialize();

    _contentTypeService.Verify(s => s.Save(It.Is<IContentType>(ct => ct.Alias == alias), It.IsAny<int>()), Times.Once);
}

此代码创建一个模拟 IContentType,当调用 GetComposition 时,它将别名设置为 aldusBlogComposition。然后 Verify 应该检查 Save 方法 运行s 一次,第一个参数是别名 属性 设置为 aldusBlogComposition.[=34 的 IContentType =]

当我 运行 测试时,这会抛出一个错误(如下),我怀疑这意味着模拟没有在 Verify 方法调用中使用。

Object reference not set to an instance of an object.

我错过了什么?

编辑:

错误是作为 contentTypeService.Verify(s => s.Save(It.Is<IContentType>(ct => ct.Alias == alias), It.IsAny<int>()), Times.Once); 调用的一部分抛出的。我唯一能看到的是 nullct - 如果我将其换成 It.IsAny<IContentType>() 则不会抛出错误。我明白什么是空引用,但我不明白为什么参数为空。

完整 class供参考:

测试class:

using Moq;
using NUnit.Framework;
using Papermoon.Umbraco.Aldus.Core.Components;
using Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes.Interfaces;
using Papermoon.Umbraco.Aldus.Core.Services.Interfaces;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace Papermoon.Umbraco.Aldus.Core.Tests.Components
{
    [TestFixture]
    public class ContentTypeComponentTests
    {
        private Mock<IContentTypeService> _contentTypeService;

        private Mock<IAldusContentTypeContainerService> _contentTypeContainerService;

        private Mock<IBlogContentTypeFactory> _blogContentTypeFactory;

        private ContentTypeComponent _component;

        [SetUp]
        public void SetUp()
        {
            _contentTypeService = new Mock<IContentTypeService>();

            _contentTypeContainerService = new Mock<IAldusContentTypeContainerService>();

            _blogContentTypeFactory = new Mock<IBlogContentTypeFactory>();

            _component = new ContentTypeComponent(_contentTypeService.Object, _contentTypeContainerService.Object, _blogContentTypeFactory.Object);
        }

        [Test]
        public void Initialize_WhenCalled_GetAldusContainer()
        {
            _component.Initialize();

            _contentTypeContainerService.Verify(s => s.GetContainer("Aldus", 1, -1));
        }

        [Test]
        public void Initialise_AldusContainerExists_GetAldusCompositionContainer()
        {
            _contentTypeContainerService
                .Setup(s => s.GetContainer("Aldus", 1, -1))
                .Returns(new EntityContainer(Constants.ObjectTypes.DocumentType)
                {
                    Id = 1
                });

            _component.Initialize();

            _contentTypeContainerService.Verify(s => s.GetContainer("Aldus Compositions", 2, 1));
        }

        [Test]
        public void Initialise_AldusContainerDoesNotExist_DoNoGetAldusCompositionsContainer()
        {
            _contentTypeContainerService
                .Setup(s => s.GetContainer("Aldus", 1, -1))
                .Returns((EntityContainer) null);

            _component.Initialize();

            _contentTypeContainerService.Verify(s => s.GetContainer("Aldus Compositions", 2, It.IsAny<int>()), Times.Never());
        }

        [Test]
        [TestCase("aldusBlogComposition")]
        public void Initialise_WhenCalled_SavesComposition(string alias)
        {
            Mock<IContentType> contentType = new Mock<IContentType>();

            _blogContentTypeFactory
                .Setup(f => f.GetComposition())
                .Callback(() => { contentType.SetupProperty(ct => ct.Alias, alias); });

            _component.Initialize();

            _contentTypeService.Verify(s => s.Save(It.IsAny<IContentType>(), It.IsAny<int>()), Times.Once);
        }

        [Test]
        public void Initialise_WhenCalled_SavesBlogContentType()
        {
            Mock<IContentType> contentType = new Mock<IContentType>();

            contentType.SetupProperty(ct => ct.Alias, "aldus");

            _blogContentTypeFactory
                .Setup(f => f.GetContentType())
                .Returns(contentType.Object);

            _component.Initialize();

            _contentTypeService.Verify(s => s.Save(contentType.Object, It.IsAny<int>()), Times.Once);
        }
    }
}

组件class:

using Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes.Interfaces;
using Papermoon.Umbraco.Aldus.Core.Services.Interfaces;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace Papermoon.Umbraco.Aldus.Core.Components
{
    public class ContentTypeComponent : IComponent
    {
        private readonly IAldusContentTypeContainerService _contentTypeContainerService;

        private readonly IContentTypeService _umbracoContentTypeService;

        private EntityContainer _aldusContainer;

        private readonly IBlogContentTypeFactory _blogContentTypeFactory;

        public ContentTypeComponent(
            IContentTypeService umbracoContentTypeService,
            IAldusContentTypeContainerService contentTypeContainerService,
            IBlogContentTypeFactory blogContentTypeFactory)
        {
            _umbracoContentTypeService = umbracoContentTypeService;

            _contentTypeContainerService = contentTypeContainerService;

            _blogContentTypeFactory = blogContentTypeFactory;
        }

        public void Initialize()
        {
            InitialiseContainers();
            InitialiseContentTypes();
        }

        private void InitialiseContainers()
        {
            _aldusContainer = _contentTypeContainerService.GetContainer("Aldus", 1);

            if (_aldusContainer != null)
            {
                _contentTypeContainerService.GetContainer("Aldus Compositions", 2, _aldusContainer.Id);
            }
        }

        private void InitialiseContentTypes()
        {
            IContentType blogContentTypeComposition = _blogContentTypeFactory.GetComposition();

            _umbracoContentTypeService.Save(blogContentTypeComposition);

            IContentType blogContentType = _blogContentTypeFactory.GetContentType();

            _umbracoContentTypeService.Save(blogContentType);
        }

        public void Terminate() { }
    }
}

还有博客工厂class:

using System.Collections.Generic;
using System.Linq;
using Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes.Interfaces;
using Papermoon.Umbraco.Utils.Services.Interfaces;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes
{
    public class BlogContentTypeFactory : ContentTypeFactory, IBlogContentTypeFactory
    {
        public BlogContentTypeFactory(
            IContentTypeService contentTypeService,
            IPapermoonContentTypeService papermoonContentTypeService,
            IPapermoonContentTypeContainerService papermoonContentTypeContainerService,
            IPapermoonTemplateService papermoonTemplateService)
            : base(
                contentTypeService,
                papermoonContentTypeService,
                papermoonContentTypeContainerService,
                papermoonTemplateService) { }

        public IContentType GetComposition()
        {
            Composition = ContentTypeService.Get("aldusBlogComposition");

            if (Composition == null)
            {
                Composition = new ContentType(AldusCompositionsContainer.Id);
            }

            Composition.Name = "Aldus Blog Composition";
            Composition.Alias = "aldusBlogComposition";
            Composition.Description = "A composition for the Aldus blog listing.";
            Composition.Icon = "icon-settings";

            return Composition;
        }

        public IContentType GetContentType()
        {
            ContentType = ContentTypeService.Get("aldusBlog");

            if (ContentType == null)
            {
                ContentType = new ContentType(AldusContainer.Id);
            }

            ContentType.Name = "Blog";
            ContentType.Alias = "aldusBlog";
            ContentType.Description = "Aldus blog listing.";
            ContentType.Icon = "icon-article";

            ContentType.AllowedTemplates = PapermoonTemplateService.Get(new [] { "AldusBlog" });

            ContentType.SetDefaultTemplate(ContentType.AllowedTemplates.First());

            ContentType.ContentTypeComposition =
                PapermoonContentTypeService.GetCompositions(ContentType, new List<string> {"aldusBlogComposition"});

            return ContentType;
        }
    }
}

最后,内容类型工厂 class:

using System.Collections.Generic;
using System.Linq;
using Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes.Interfaces;
using Papermoon.Umbraco.Utils.Services.Interfaces;
using Umbraco.Core.Models;
using Umbraco.Core.Services;

namespace Papermoon.Umbraco.Aldus.Core.Factories.ContentTypes
{
    public class BlogContentTypeFactory : ContentTypeFactory, IBlogContentTypeFactory
    {
        public BlogContentTypeFactory(
            IContentTypeService contentTypeService,
            IPapermoonContentTypeService papermoonContentTypeService,
            IPapermoonContentTypeContainerService papermoonContentTypeContainerService,
            IPapermoonTemplateService papermoonTemplateService)
            : base(
                contentTypeService,
                papermoonContentTypeService,
                papermoonContentTypeContainerService,
                papermoonTemplateService) { }

        public IContentType GetComposition()
        {
            Composition = ContentTypeService.Get("aldusBlogComposition");

            if (Composition == null)
            {
                Composition = new ContentType(AldusCompositionsContainer.Id);
            }

            Composition.Name = "Aldus Blog Composition";
            Composition.Alias = "aldusBlogComposition";
            Composition.Description = "A composition for the Aldus blog listing.";
            Composition.Icon = "icon-settings";

            return Composition;
        }

        public IContentType GetContentType()
        {
            ContentType = ContentTypeService.Get("aldusBlog");

            if (ContentType == null)
            {
                ContentType = new ContentType(AldusContainer.Id);
            }

            ContentType.Name = "Blog";
            ContentType.Alias = "aldusBlog";
            ContentType.Description = "Aldus blog listing.";
            ContentType.Icon = "icon-article";

            ContentType.AllowedTemplates = PapermoonTemplateService.Get(new [] { "AldusBlog" });

            ContentType.SetDefaultTemplate(ContentType.AllowedTemplates.First());

            ContentType.ContentTypeComposition =
                PapermoonContentTypeService.GetCompositions(ContentType, new List<string> {"aldusBlogComposition"});

            return ContentType;
        }
    }
}

此外,我尝试添加对 .Returns 的调用,但仍然看到错误。我试过以下方法:

回调后:

_blogContentTypeFactory
    .Setup(f => f.GetComposition())
    .Callback(() => { contentType.SetupProperty(ct => ct.Alias == alias); })
    .Returns(contentType.Object);

作为 return 的一部分的赋值:

_blogContentTypeFactory
    .Setup(f => f.GetComposition())
    .Returns(contentType.SetupProperty(ct => ct.Alias, alias).Object);

是否因为您在 Mock 工厂中缺少 .Returns 用法?

    contentType.SetupProperty(ct => ct.Alias, alias)

   _blogContentTypeFactory
        .Setup(f => f.GetComposition())
        .Returns(contentType.SetupProperty);

如果没有看到围绕具体实例化的更多代码,很难判断 class。

有几种方法可以配置模拟及其 属性。

以下使用LINQ to Mocks appraoch

[Test]
[TestCase("aldusBlogComposition")]
public void Initialise_WhenCalled_SavesComposition(string alias) {
    //Arrange
    IContentType contentType = Mock.Of<IContentType>(_ => _.ALias == alias);

    _blogContentTypeFactory
        .Setup(_ => _.GetComposition())
        .Returns(contentType);

    //Act
    _component.Initialize();

    //Assert
    _contentTypeService.Verify(s => s.Save(It.Is<IContentType>(ct => ct.Alias == alias), It.IsAny<int>()), Times.Once);
}

您甚至可以验证它与传递的返回实例相同

//...omitted for brevity

//Assert
_contentTypeService.Verify(s => s.Save(contentType, It.IsAny<int>()), Times.Once);

//...

原来 Verify 设置是正确的,但是 return 从 GetContentType 方法中编辑了一个空值,我只需要设置一个 return :

_blogContentTypeFactory
    .Setup(f => f.GetContentType())
    .Returns(Mock.Of<IContentType>());