0

I have already read some great questions and answer about this issue for example Cannot Use ConfigurationManager inside Unit Test Project

and How to mock ConfigurationManager.AppSettings with moq

For now the solution I'm using in my unit tests is as suggested an App.config file for unit test project. and it is working fine.

my goal is to dynaically load plugin (*.dll) into my C# project.

here is my original App.config

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicFormattedKeyToken=b77a5c561934e089">
      <section name="IQCMain.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicFormattedKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
   <section name="CalibrationToolsSection" type="sec.Calibration.Configuration.CalibrationToolsSection,sec" />
  </configSections>
  <CalibrationToolsSection>
    <!-- This section contains the calibration tools' loading-enabling options, and versioning parameters as follows:
         toolname - the calibration tool's name as returned by GetName() in ICalibrationPlugin (for static tool) or as appears in the records (for auto-gen tool)
         isvisible - if set to "false" the tool will not be loaded. Deafult value: true
         version - if set to "false" the tool will not be loaded. Deafult value: true   /-->
    <!--IpuToolCalibration-->
    <IpuToolCalibrations>
      <add ipu="p1">
        <CalibrationToolsLoadingSettings>
          <add toolname="AE" version="1" isvisible="false"/>
          <add toolname="AE2" version="1" isvisible="true"/>
        </CalibrationToolsLoadingSettings>
      </add>
      <add ipu="p2">
        <CalibrationToolsLoadingSettings>
          <add toolname="AE" version="1" isvisible="false"/>
          <add toolname="AE2" version="1" isvisible="true"/>
        </CalibrationToolsLoadingSettings>
      </add>
      <add ipu="p3">
        <CalibrationToolsLoadingSettings>
          <add toolname="AE" version="1" isvisible="false"/>
          <add toolname="AE2" version="1" isvisible="true"/>
        </CalibrationToolsLoadingSettings>
      </add>
      <add ipu="p4">
        <CalibrationToolsLoadingSettings>
          <add toolname="AE" version="1" isvisible="false"/>
          <add toolname="AE2" version="1" isvisible="true"/>
        </CalibrationToolsLoadingSettings>
        <PalAteConnectivitySettings>
          <add pal-uuid="1111" connect-ate="true"  name="p4_wb" />
          <add pal-uuid="2222" connect-ate="true"  name="p4_lsc"  />
          <add pal-uuid="3333" connect-ate="true"  name="p4_blc"  />
          <add pal-uuid="4343" connect-ate="true"  name="p4_disparity"  />
          <add pal-uuid="9999" connect-ate="true"  name="p4_gridbaseob" />
  </PalAteConnectivitySettings>
      </add>
      <add ipu="p5">
    <CalibrationToolsLoadingSettings>
          <add toolname="ACMCmc" version="1" isvisible="true"/>
          <add toolname="ACM3A" version="1" isvisible="true"/>
          <add toolname="AE" version="1" isvisible="false"/>
          <add toolname="AE2" version="1" isvisible="true"/>
          <add toolname="llolo" version="1" isvisible="true" />
          <add toolname="lalla" version="1" isvisible="true" />
   </CalibrationToolsLoadingSettings>
   <PalAteConnectivitySettings>
          <add pal-uuid="32398" connect-ate="true"  name="p5_wb" />
          <add pal-uuid="53711" connect-ate="true"  name="p5_lsc"  />
          <add pal-uuid="40661" connect-ate="true"  name="p5_blc"  />
          <add pal-uuid="55093" connect-ate="true"  name="p5_disparity"  />
          <add pal-uuid="46517" connect-ate="true"  name="p5_gridbaseob" />
  </PalAteConnectivitySettings>
 </IpuToolCalibrations>
 </CalibrationToolsSection>
  <appSettings>
    <add key="DisableCalibrationCheck" value="false" />
    <add key="ClientSettingsProvider.ServiceUri" value="" />
  </appSettings>

I saw that you can dynamically create the collections, for example

ConfigurationManager.AppSettings["mykey"] = "myvalue";

right now i'm using classes like so using System.Configuration;

namespace Manager.Calibration.Configuration
{
    public class CalibrationToolsSection : ConfigurationSection
    {
        public static readonly string CALIBRATION_SECTION_NAME = "CalibrationToolsSection";

        [ConfigurationProperty("IpuToolCalibrations")]
        public IpuToolCalibrationCollection IpuToolCalibrations
        {
            get
            {
                return ((IpuToolCalibrationCollection)this["IpuToolCalibrations"]);
            }
            set
            {
                this["IpuToolCalibrations"] = value;
            }
        }
    }
}

and also

public class IpuToolCalibrationCollection : ConfigurationElementCollection
    {
        public IpuToolCalibration this[int index]
        {
            get { return base.BaseGet(index) as IpuToolCalibration; }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new IpuToolCalibration();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((IpuToolCalibration)element).Ipu;
        }
    }

and so on

public class IpuToolCalibration : ConfigurationElement
    {
        public static readonly string IPU_TOOL_CALIBRATION_SECTION_NAME = "IpuToolCalibration";

        [ConfigurationProperty("ipu", IsRequired = true)]
        public string Ipu
        {
            get { return (string)this["ipu"]; }
            set { this["ipu"] = value; }
        }

        [ConfigurationProperty("CalibrationToolsLoadingSettings")]
        public ToolSettingElementCollection ToolsLoadingSettingsCollection
        {
            get
            {
                return ((ToolSettingElementCollection)this["CalibrationToolsLoadingSettings"]);
            }
            set
            {
                this["CalibrationToolsLoadingSettings"] = value;
            }
        }


        [ConfigurationProperty("PalAteConnectivitySettings")]
        public FilterAteConnectivityElementCollection PalAteConnectivityCollection
        {
            get
            {
                return ((FilterAteConnectivityElementCollection)this["PalAteConnectivitySettings"]);
            }
            set
            {
                this["PalAteConnectivitySettings"] = value;
            }
        }

    }
}

Can anyone explain how can I create those from code, since they are all static classes without constructors, how am I suppose to mock them, i'm using NSubstitute. if for example i want to load part of the plugins and not all of them?

Community
  • 1
  • 1
Gilad
  • 6,437
  • 14
  • 61
  • 119

1 Answers1

-1

I think that anytime you're faced with trying to unit test any piece of code that has a static implementation you need to create a wrapper of some sort that can then be injected where needed - in some ways it's a variation 'decorator pattern'. The code sample I'm providing is to just illustrate the point:

public class FileReaderService : IFileReaderService
{
    public string GetFileAsString(string fileName)
    {
        if(!File.Exists(fileName))
            throw new ArgumentException("File Path does not exist.");

        return File.ReadAllText(fileName);
    }
}

public interface IFileReaderService
{
    string GetFileAsString(string fileName);
}

I'm wrapping the static function File.ReadAllText with a service called FileReaderService and have an interface IFileReaderService. Now I can inject the service anywhere I need it and then be able to Mock and write unit tests accordingly. I hope this gives you an idea of how you can refactor your code to do virtually the same.

TheRock
  • 1,513
  • 1
  • 18
  • 19
  • In my opinion this is not the ame problem since this is not the classic case of one static class in which you solution works fine. This is the case where the static class calls many other classes as a part of its initialization... Which we are not exposed to. The those other classes needs to be read and alocated from a file. So wrapping around doesn't do the trick here.. Again in my opinion. – Gilad Oct 24 '16 at 16:58
  • I'm not too familiar with NSubstitute but think it's probably the same as Moq or Rhinomocks so mocking static calls is not possible: http://stackoverflow.com/a/5864211/5121114 I think you have to figure out a way to refactor your code so that it does become possible. That's if you want to be able to Mock and write Unit Tests. Your other option is to break the code up in a way where you're static calls have no dependencies and can to tested on their own. Other than that you have dependencies in your code and are writing Integration/EndToEnd Tests rather than Unit Tests. – TheRock Oct 24 '16 at 17:32