隔离用于单元测试的服务结构ServiceContext



我的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创建对象,因为它们有内部构造函数。

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

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

例如,您可以使用MockStatelessServiceContextFactory.Default来获取StatelessServiceserviceContext mock。

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

我在打印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属性,但你可以将它扩展到你喜欢的东西(显然)。

最新更新