如何使用 ServiceFabric.Mocks Nuget 包模拟配置和数据包的 CodePackageActivationContext

How do you Mock CodePackageActivationContext for Config and Data Package using ServiceFabric.Mocks Nuget package

这是

的后续问题

我正在使用 Xunit 和 ServiceFabric.Mocks 对我的应用程序进行单元测试。 我的 Service Fabric 应用程序如下所示:

namespace SearchService
{
    internal sealed class SearchServiceClass : StatelessService
    {
        //variables defined followed by constructor
        private string jsonStr;
        public SearchServiceClass(StatelessServiceContext context)
            : base(context)
        {
            try
            {
                var section = Context.CodePackageActivationContext
                .GetConfigurationPackageObject("Config")
                .Settings
                .Sections[_configSectionName];
                var _parameters = section.Parameters.ToDictionary(kv => kv.Name, kv => kv.Value);
                //_parameters used to set further variables in the constructor
                var dataPackage = Context.CodePackageActivationContext
                .GetDataPackageObject("Data");
                jsonStr = File.ReadAllText(dataPackage.Path + @"\data.json");
                //removed code for brevity
            }
            catch
            {
                //exception handling code
                throw;
            }
        }
        
        public bool IsDataJsonLoaded
        {
            get
            {
                return !(jsonStr == null);
            }
        }
    }
}

初始单元测试如下所示:

using Xunit;
using ServiceFabric.Mocks;

namespace App.UnitTests
{
    public class SearchServiceTest
    {
        [Fact]
        public void SearchServiceClassConstructor()
        {
            var searchServiceClass = new SearchServiceClass(MockStatelessServiceContextFactory.Default);
            //assert:
            Assert.True(searchServiceClass.IsDataJsonLoaded);
        }
    }
}

当我运行这个单元测试时,出现“System.NullReferenceException: 对象引用未设置到对象的实例。”抛出异常。这是因为在单元测试设置中,"Context.CodePackageActivationContext.GetDataPackageObject("Data");" returns 对“dataPackage”为空。因此对 IsDataJsonLoaded returns 'False'.

的断言

我在 Whosebug 上四处搜索,发现这个答案最能回答我的问题: 但是在使用这个答案中的代码时,我的单元测试看起来像这样:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServiceFabric.Mocks;
using SearchService;
using Xunit;
using static ServiceFabric.Mocks.MockConfigurationPackage;

namespace App.UnitTests
{
    public class SearchServiceTest
    {
        [Fact]
        public void SearchServiceClassConstructor()
        {
            //Arrange
            const string path = "../SearchService/PackageRoot/Data"; //The Data Package lives in the SearchService project, specifically PackageRoot/Data
            const string name = "Data";
            const string version = "1.0.0";
            const string serviceManifestName = "SearchServicePkg";
            const string serviceManifestVersion = "1.0.0";
            var dataPackageDescription = MockDataPackage.CreateDataPackageDescription(name, version, serviceManifestName, serviceManifestVersion, path);
            var dataPackage = MockDataPackage.CreateDataPackage(path, dataPackageDescription);

            //build ConfigurationSectionCollection
            var configSections = new ConfigurationSectionCollection();
            //Build ConfigurationSettings
            var configSettings = CreateConfigurationSettings(configSections);
            //add one ConfigurationSection
            ConfigurationSection configSection = CreateConfigurationSection("SearchServiceConfigSection");
            configSections.Add(configSection);

            //add one Parameters entry
            ConfigurationProperty parameter = CreateConfigurationSectionParameters(nameof(parameter.Name), nameof(parameter.Value));
            configSection.Parameters.Add(parameter);
            var Context = MockCodePackageActivationContext.Default;

            var searchServiceClass = new SearchServiceClass(MockStatelessServiceContextFactory.Default);
            //Act
            Context.SetDataPackage(dataPackage);
            //Assert:
            Assert.True(searchServiceClass.IsDataJsonLoaded);
        }
        
    }
}

但是现在编译器抛出错误,指出在命名空间中找不到“ConfigurationSection”和“ConfigurationProperty”。

我走在正确的轨道上吗?如果我这样做,我是否能够完全模拟单元测试环境中实际 StatelessService 的配置和数据包。我该如何首先修复这些错误?

编辑:根据下面 LoekD 的回复,我已经更新到 ServiceFabric.Mocks v4.2.8,并实现了额外的代码来模拟数据包。但是 NullReferenceException 仍然存在。此错误对应于 SearchServiceClass 的构造函数中的“dataPackage.Path”行。而现在有一个单独的“MockCodePackageActivationContext.Default;” context 和单独的“MockStatelessServiceContextFactory.Default”上下文传递给单元测试中 SearchServiceClass 的构造函数,CodePackageActivation 在 Mock StatelessServiceContext 中不存在。不确定这是否有意义......但本质上,这似乎导致构造函数中 dataPackage.Path 引用中的“NullReferenceException”。有什么解决办法吗?

你走对了!

  • 第 1 步

ConfigurationSectionConfigurationProperty 在 SF 命名空间 System.Fabric.Description 中。为命名空间

添加一个using
  • 第 2 步

请升级到 ServiceFabric.Mocks v4.2.8。这增加了对 MockCodePackageActivationContext 上模拟 DataPackage 属性 的支持。 有关示例,请参阅 this page

  • 步骤 3.

使用创建的上下文作为构造函数参数。 更改此代码:

var searchServiceClass = new SearchServiceClass(MockStatelessServiceContextFactory.Default);
            

进入这个:

var activationContext = new MockCodePackageActivationContext(
                "fabric:/MockApp",
                "MockAppType",
                "Code",
                "1.0.0.0",
                Guid.NewGuid().ToString(),
                @"C:\logDirectory",
                @"C:\tempDirectory",
                @"C:\workDirectory",
                "ServiceManifestName",
                "1.0.0.0")
            {
                ConfigurationPackage = configPackage
            };
activationContext.SetDataPackage(dataPackage);

var newUri = new Uri("fabric:/MockApp/SearchService");
var serviceTypeName = "SearchServiceType";
var partitionId = Guid.NewGuid();
var replicaId = long.MaxValue;

var serviceContext = MockStatelessServiceContextFactory.Create(activationContext, serviceTypeName, newUri, partitionId, replicaId);
var searchServiceClass = new SearchServiceClass(serviceContext);