Binu Vijayan
Binu Vijayan

Reputation: 803

Isolating Service Fabric ServiceContext for unit testing

I have a method in my Service fabric Stateless service application which get the configuration stored in Settings.xml from ServiceContext

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;
}    

In the above code I split the code into many lines for easier understanding, actually i use the commented code in my application.

I need to write unit test for this method. I could mock ServiceContext and ICodeActivationContext

But i could not create objects for ConfigurationSettings and ConfigurationPackage since they have internal constructors.

How do I isolate these classes in my unit test. Or should i exclude the service context part from my unit test.

Upvotes: 2

Views: 2605

Answers (3)

pberggreen
pberggreen

Reputation: 958

Now you can use a NuGet package named ServiceFabric.Mocks, which offers mocks for most Service Fabric classes.

For example, you can use MockStatelessServiceContextFactory.Default to get a StatelessServiceContext mock.

Upvotes: 3

Philip Patrick
Philip Patrick

Reputation: 341

I would create an interface that returns parameters from service fabric (one of them is the connection string). Then a class that implements the interface the way you wrote in the question. And that interface can be mocked used in unittesting. The result is - that you cannot test the method that actually read from service parameters, but at least you can test everyone who uses it without the need to mock ServiceContext and alike.

Upvotes: 1

mark_h
mark_h

Reputation: 5477

I had an almost identical issue with the System.Printing PrintSystemJobInfo class, it has a sealed constructor so it proved very difficult to mock. I assume you are creating an interface that closely resembles the class you wish to mock, then creating a wrapper for the actual class that implements the interface.

A solution to your problem is to pass the parent class as a parameter in the constructor of the child class (so the child class has access to the parent methods and can build the real implementation that you intend to wrap).

The following code demonstrates how I did it with 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; }
        }
    }
}

}

I have tried to keep this as simple as possible so I have only wrapped IsBlocked property, but you could extend it to what ever you liked (obviously).

Upvotes: 0

Related Questions