如何对机器特定行为进行单元测试?

时间:2009-05-14 20:27:01

标签: .net unit-testing nunit

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

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

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

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

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

  1. 测试表现不同 取决于环境 在

  2. 中运行
  3. 我基本上是在结合测试 对方法,因为你需要 知道方法的内部结构 设置了两个案例。

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

    任何建议都会有所帮助!

5 个答案:

答案 0 :(得分:2)

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

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类似,但使用的是界面。通常我会成为界面解决方案的粉丝,但感觉它会让生产代码和配置变得更复杂。我的解决方案肯定不那么“纯粹”,但可能更实用。我会让你判断:)

答案 1 :(得分:1)

你能隐藏抽象接口背后的静态方法吗?

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

答案 2 :(得分:1)

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

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

e.g。

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

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

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

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

如果您不想将接口传递给静态方法,您可能会尝试使用单例模式实现对象,然后调用此接口。然后,您拥有的静态方法可以使用单例来访问当前界面,尽管这种方法有点混乱,所以我可能会选择前者。

E.g。

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

虽然你需要注意你是否正在使用像这样的单例进行任何多线程处理。

答案 3 :(得分:0)

我不明白为什么你不能嘲笑这个。您的方法应该使用bool SupportsIPv6参数,并在实际代码中传递System.Net.Sockets.Socket.OSSupportsIPv6。然后,使用参数同时为true和false来测试方法,并确保它为true和false情况都喷出正确的格式。瞧,完了。

答案 4 :(得分:0)

有时确实发生了很难进行有用的单元测试。我不相信这是其中一个案例,但值得记住。

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

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

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