【问题标题】:How do I unit test for machine specific behaviour?如何对机器特定行为进行单元测试?
【发布时间】:2009-05-14 20:27:00
【问题描述】:

我正在测试一个静态方法,该方法在检查代理和主机名以及各种事情后构建一个 URL 字符串。此方法在内部依赖于静态标志System.Net.Sockets.Socket.OSSupportsIPv6。因为它是静态的,所以我无法模拟这种依赖关系。

编辑:(这被简化了很多......我不能用标志修改方法的结构以接受真/假)。

在 XP 开发机器上,所讨论的方法返回一个可预测的字符串结果(在这种情况下,http://hostname/....)。我们支持 IPv6 的构建服务器返回完全不同的结果(它给了我类似http://192.168.0.1:80/....)。请不要问为什么 - 关键是有两种不同的输出类型因操作系统依赖性而异。

测试需要验证返回的主机名或 IP 地址是否有效。输出很容易检查。问题是我只能得到一个可能的输出,这取决于运行测试的机器。

在这种情况下编写测试的最佳做法是什么?我是否在我的测试中放置了一个 if 语句来检查标志,然后查找两个不同的输出?这对我来说似乎很粗略,因为

  1. 测试表现不同 取决于环境 跑进去

  2. 我基本上是在耦合测试 到方法,因为你需要 了解方法的内部结构 设置两种情况。

我是否设置了两个测试,每个环境一个,如果它们在错误的环境中就可以通过?我是否编写了一些复杂的正则表达式,可以分离出它得到什么样的结果,并使用更多的 if/else 逻辑来验证它?

任何建议都会有所帮助!

【问题讨论】:

    标签: .net unit-testing nunit


    【解决方案1】:

    您可以通过介绍中间人来伪造电话。例如:

    public static class NetworkUtils
    {
        private static bool? forcedIPv6Support;
    
        /// <summary>
        /// Use this instead of Socket.OSSupportsIPv6 to
        /// allow for testing on different operating systems.
        /// </summary>
        public static SupportsIPv6
        {
            get { return forcedIPv6Support ?? Socket.OSSupportsIPv6; }
        }
    
        /// <summary>Only use in testing!</summary>
        public static void ForceIPv6Support(bool? value)
        {
            forcedIPv6Support = value;
        }
    }
    

    这并不完全令人愉快,但它可以让你做你需要做的事情。

    ChrisW's answer 类似,但使用了一个接口。通常我会喜欢接口解决方案,但感觉它使生产代码和配置变得更加复杂。我的解决方案肯定不那么“纯粹”,但可能更务实。我会让你判断:)

    【讨论】:

    • 您的解决方案更短,因此(鉴于“实体不得超出必要性”)更好。
    • 这两个都是很好的建议,我可以在不破坏 prod 代码的情况下进行构建。
    【解决方案2】:

    你能把静态方法隐藏在抽象接口后面吗?

    interface ISupportIPv6
    {
      bool supported { get; }
    }
    
    //class for unit testing
    class TestSupportsIPv6 : ISupportIPv6
    {
      public bool supported { get { return true; } }
    }
    
    //another class for more unit testing
    class TestDoesNotSupportIPv6 : ISupportIPv6
    {
      public bool supported { get { return false; } }
    }
    
    //class for real life (not unit testing)
    class OsSupportsIPv6 : ISupportIPv6
    {
      public bool supported { get {
        return System.Net.Sockets.Socket.OSSupportsIPv6; } }
    }
    

    【讨论】:

      【解决方案3】:

      您可以考虑将所有可能更改的环境设置包装在实现接口的单独类中。然后可以将其传递给您编写的静态方法,然后在需要各种设置时回调接口。

      这将允许您在测试时模拟环境设置以标准化,以防止您看到的问题。它还可以让您改变设置以测试不同的条件。

      例如

      public interface IEnvironment
      {
          public bool SupportsIPV6 { get; }
      }
      

      然后你的静态方法就变成了。

      public static void DoSomething(IEnvironment environment)
      {
          if(environment.SupportsIPV6)
          {
              ...
          }
      }
      

      然后提供一个 IEnvironment 实现,它调用静态 System.Net 方法以获取实际使用设置,但提供一个模拟实现,用于使用已知值进行测试。

      如果您不想将接口传递给静态方法,您可以考虑使用单例模式实现一个对象,然后调用该接口。然后,您拥有的静态方法可以使用单例访问当前接口的任何内容,尽管这种方法有点混乱,所以我可能会使用前者。

      例如

      public Environment : IEnvironment
      {
        private static IEnvironment current = new Environment();
        public static IEnvironment Current
        {
          get { return current; }
          set { current = value; }
        }
      
        public bool SupportsIPV6
        {
          return System.Net.....
        }
      }
      

      然后使用静态方法调用它。

      public static void DoSomething(IEnvironemnt environment)
      {
          if(Environment.Current.SupportsIPV6)
          {
              ...
          }
      }
      

      你可以通过

      在你的测试中改变它
      Environment.Current = new MockedEnvironment();
      

      尽管您需要注意是否正在使用这样的单例进行任何多线程。

      【讨论】:

        【解决方案4】:

        我不明白你为什么不能嘲笑这个。您的方法应采用bool SupportsIPv6 参数,在实际代码中,您将其传递给System.Net.Sockets.Socket.OSSupportsIPv6。然后你测试你的方法,你的参数是真假,并确保它为真假情况吐出正确的格式。瞧,完成了。

        【讨论】:

        • 嗯,这使得呼叫站点难以测试……基本上,它通过了降压。这可能不是问题,诚然...
        • 那绝对是理想的。我实际上简化了很多,有几个外部调用,它依赖的标志实际上是在一个我无法控制的 .dll 中。
        • 我明白了。那么,我想那是一种耻辱。
        【解决方案5】:

        有时确实会发生这样的情况,即很难进行有用的单元测试。我不确定这是其中一种情况,但值得记住。

        也就是说,现在,定义“有效”是什么意思?您当然可以测试它的格式是否正确:[0-9]{1,3} 是一个正则表达式的开始。

        您可以使用 ICMP 回显数据包(或 ping(8))测试它是否可以访问。

        如果您可以定义“有效”对您意味着什么,那应该定义单元测试。

        【讨论】:

        • 这是一个值得肯定的观点。在这种情况下,问题更多的是关于测试的结构——我绝对知道输出何时有效。但根据您运行测试的位置,您只能测试其中一个输出。
        猜你喜欢
        • 1970-01-01
        • 2015-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-17
        • 1970-01-01
        • 2012-01-08
        • 2019-11-02
        相关资源
        最近更新 更多