GregJF
GregJF

Reputation: 466

Moq an object in a static class

I can't get Moq to mock an object that gets created in a static method. Here is my moq and code

code:

public interface IConfigHelper
{
    string GetConfiguration(string sectionName, string elementName);
}

public class ConfigHelper : IConfigHelper
{
    public ConfigHelper() { }

    public virtual string GetConfiguration(string sectionName, string elementName)
    {
        string retValue = String.Empty;
        //Does things to get configuration and return a value    
        return retValue;
    }
}

public class myRealClass
{
    public myRealClass(){}
    public string myworkingMethod()
    {
        var retValue = String.Empty;
        retValue = utilSvc.GetConfigurationValue();
        return retValue;
    }
}

public static class utilSvc
{
    public static string GetConfigurationValue()
    {
        ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
        return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
    }
}

the Test using Moq

[TestFixture(TestName = "Tests")]
public class Tests 
{
    private Mock<IConfigHelper> configHelperMOCK;
    [SetUp]
    public void Setup()
    {
        configHelperMOCK = new Mock<IConfigHelper>();
    }

    [Test]
    public void serviceIsBPManagementForValidSource()
    {
        //Arrange
        string sectionName = "sectionName/sectionElement";
        string clinicalElementName = "ClinicalSystem";
        string clinicalElementValue = "Zedmed";
        configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue);

        //act
        // the call to myRealClass

        //assert
        // test assertions
    }
}

The issue that I am having is with this line:

ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED

I cannot get the moq to Mock the object. I do not want the code to read the config file. I wish to moq away this instance of ConfigHelper

Upvotes: 3

Views: 6260

Answers (3)

AlanT
AlanT

Reputation: 3663

You can't wrap the static class/method but you can redirect it

public static class UtilSvc
{
     static UtilSvc()
     {
         CreatorFunc = () => new ConfigHelper();
     }

     public static Func<IConfigHelper> CreatorFunc { get; set; }

     public static string GetConfigurationValue()
     {
         var configUtil = CreatorFunc();
         return configUtil.GetConfiguration("sectionName/sectionElement", 
                                            "ClinicalSystem");
     }
}

and then in the test

//...
private Mock<IConfigHelper> configHelperMOCK;

[SetUp]
public void Setup()
{
    configHelperMOCK = new Mock<IConfigHelper>();
    UtilService.CreatorFunc = () => configHelperMOCK.Object;
}
//...

Upvotes: 1

Nkosi
Nkosi

Reputation: 247088

Avoid coupling your code to static classes, which in most cases cause you code be to difficult to maintain and test.

Follow the Explicit Dependencies Principle

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

Give the article a read. It is short and very informative.

If you want to keep the static class then you wrap the static class behind an abstraction.

public interface IUtilSvc {
    string GetConfigurationValue();
}

public class utilSvcWrapper : IUtilSvc {
    public string GetConfigurationValue() {
        return utilSvc.GetConfigurationValue(); //Calling static service
    }
}

Or another option is that utlSvc does not have to be static if can be injected into dependent classes

public class utilSvc : IUtilScv {
    private readonly IConfigHelper configUtil;

    public utilSvc(IConfigHelper configHelper) {
        configUtil = configHelper;
    }

    public string GetConfigurationValue() {
        return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
    }
}

Inject the IUtilScv into the dependent class so that it is no longer dependent on static class.

public class myRealClass {
    private readonly IUtilScv utilSvc;

    //Explicit dependency inject via constructor
    public myRealClass(IUtilScv utilSvc) {
        this.utilSvc = utilSvc;
    }

    public string myworkingMethod() {
        var retValue = utilSvc.GetConfiguration();
        return retValue;
    }
}

In that case you don't even need IConfigHelper when testing as it has also been abstracted away. And you only need to mock the dependencies needed for the test.

[TestFixture(TestName = "Tests")]
public class Tests {
    private Mock<IUtilScv> utilScvMOCK;

    [SetUp]
    public void Setup() {
        utilScvMOCK = new Mock<IUtilScv>();
    }

    [Test]
    public void serviceIsBPManagementForValidSource() {
        //Arrange
        var expectedClinicalElementValue = "Zedmed";
        utilScvMOCK
            .Setup(s => s.GetConfiguration())
            .Returns(expectedClinicalElementValue)
            .Verifiable();            

        var sut = new myRealClass(utilScvMOCK.Object);

        //Act
        var actualClinicalElementValue = sut.myworkingMethod();

        //Assert
        configHelperMOCK.Verify();
        Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue);
    }
}

Upvotes: 1

Johnny
Johnny

Reputation: 9519

You cannot mock static class. I would rather propose to inject that IConfigHelper into the myRealClass. That is the usual way how to decouple dependencies and use DI.

public class myRealClass
{
  private IConfigHelper _configHelper;  

  public myRealClass(IConfigHelper configHelper)
  {
     _configHelper = configHelper;
  }

  public string myworkingMethod()
  {
     var retValue = String.Empty;
     retValue = _configHelper.GetConfigurationValue();
     return retValue;
   }
}

Upvotes: 0

Related Questions