C# 单元测试中的配置管理器

C# 单元测试中的配置管理器,c#,unit-testing,C#,Unit Testing,我已经阅读了一些关于这个问题的好问题和答案 例如 及 目前,我在单元测试中使用的解决方案是单元测试项目的App.config文件。 它工作得很好 我的目标是动态加载插件(*.dll)到我的C#项目中 这是我的原始App.config <?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="applicationSettings" type="Sy

我已经阅读了一些关于这个问题的好问题和答案 例如

目前,我在单元测试中使用的解决方案是单元测试项目的App.config文件。 它工作得很好

我的目标是动态加载插件(*.dll)到我的C#项目中

这是我的原始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>
现在我正在使用这样的类 使用系统配置

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

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

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

    }
}
谁能解释一下我如何从代码中创建这些类,因为它们都是没有构造函数的静态类,我应该如何模拟它们,我使用的是NSubstitute。
例如,如果我想加载部分插件,而不是全部插件?

我认为,无论何时,当您面临对任何具有静态实现的代码段进行单元测试时,您都需要创建某种包装,然后可以在需要的地方注入——在某些方面,这是一种变体“装饰器模式”。我提供的代码示例只是为了说明这一点:

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

我正在用一个名为FileReaderService的服务包装静态函数File.ReadAllText,并有一个接口IFileReaderService。现在,我可以在任何需要的地方注入服务,然后可以相应地模拟和编写单元测试。我希望这能让您了解如何重构代码以实现几乎相同的功能。

在我看来,这不是ame问题,因为这不是解决方案运行良好的静态类的典型情况。在这种情况下,静态类调用许多其他类作为其初始化的一部分。。。这是我们没有接触过的。这些其他类需要从文件中读取和分配。所以,在这里,绕圈子是不行的。。同样在我看来,我对NSubstitute不太熟悉,但我认为它可能与Moq或rhinomock相同,因此模拟静态调用是不可能的:我认为您必须找到一种方法来重构代码,使之成为可能。如果您希望能够模拟和编写单元测试,就可以这样做。您的另一个选择是以一种方式分解代码,在这种方式中,静态调用没有依赖性,可以自己进行测试。除此之外,您的代码中有依赖项,并且正在编写集成/端到端测试,而不是单元测试。
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);
}