隔离Service Fabric ServiceContext以进行单元测试

时间:2016-05-18 10:47:38

标签: c# unit-testing azure-service-fabric isolation

我的Service Fabric无状态服务应用程序中有一个方法,它从ServiceContext获取存储在Settings.xml中的配置

public static string GetConnectionString()
{
    if (context == null)
        return string.Empty;

    // return context.CodePackageActivationContext.GetConfigurationPackageObject("Config").Settings.Sections["MySection"].Parameters["ConnectionString"].Value;

    ICodePackageActivationContext activationContext = context.CodePackageActivationContext;
    ConfigurationPackage configPackage = activationContext.GetConfigurationPackageObject("Config");
    ConfigurationSettings configSettings = configPackage.Settings;
    string connectionString = configSettings.Sections["ConnectionData"].Parameters["ConnectionString"].Value;
    return connectionString;
}    

在上面的代码中,我将代码分成多行以便于理解,实际上我在我的应用程序中使用了注释代码。

我需要为这个方法编写单元测试。 我可以模拟ServiceContext和ICodeActivationContext

但我无法为ConfigurationSettings和ConfigurationPackage创建对象,因为它们具有内部构造函数。

如何在单元测试中隔离这些类。或者我应该从单元测试中排除服务上下文部分。

3 个答案:

答案 0 :(得分:1)

我会创建一个从服务结构返回参数的接口(其中一个是连接字符串)。然后是一个按照你在问题中写的方式实现接口的类。并且可以模拟在单元测试中使用该接口。 结果是 - 您无法测试实际从服务参数读取的方法,但至少您可以测试使用它的每个人,而无需模拟ServiceContext等。

答案 1 :(得分:1)

现在您可以使用名为ServiceFabric.Mocks的NuGet包,它为大多数Service Fabric类提供模拟。

例如,您可以使用MockStatelessServiceContextFactory.Default来获取StatelessServiceContext模拟。

答案 2 :(得分:0)

我与System.Printing PrintSystemJobInfo类有一个几乎完全相同的问题,它有一个密封的构造函数,因此事实证明它非常难以模拟。我假设您正在创建一个非常类似于您希望模拟的类的接口,然后为实现该接口的实际类创建一个包装器。

您的问题的解决方案是将父类作为子类的构造函数中的参数传递(因此子类可以访问父方法,并且可以构建您打算包装的实际实现)。

以下代码演示了我如何使用PrintSystemJobInfo;

using System;
using System.Printing;

namespace ConsoleApplication6
{
class Program
{
    static void Main(string[] args)
    {
        var server = new LocalPrintServer();

        IPrintQueue testablePrintQueue = new RealPrintQueue(server);

        IPrintSystemJobInfo  printSystemJobInfo = testablePrintQueue.AddJob();

        var result = printSystemJobInfo.IsBlocked;

        Console.WriteLine(result);

    }

    public interface IPrintSystemJobInfo
    {
         bool IsBlocked { get; }
    }

    public interface IPrintQueue
    {
        IPrintSystemJobInfo AddJob();
    }
    public class RealPrintQueue:IPrintQueue
    {
        private PrintQueue _queue; 
        public RealPrintQueue(LocalPrintServer server)
        {
            _queue = server.DefaultPrintQueue;
        }

        public IPrintSystemJobInfo AddJob()
        {
            return new RealPrintSystemJobInfo(_queue);
        }

    }

    public class RealPrintSystemJobInfo: IPrintSystemJobInfo
    {
        private PrintSystemJobInfo job;
        public RealPrintSystemJobInfo(PrintQueue queue)
        {
            job = queue.AddJob();
        }

        public bool IsBlocked
        {
            get { return job.IsBlocked; }
        }
    }
}

}

我试图尽量保持这个简单,所以我只包含了IsBlocked属性,但你可以把它扩展到你喜欢的东西(显然)。