我应该如何测试方法中的null和/或空字符串参数?

Ger*_*och 9 string testing parameters null

通常使用带有字符串参数的方法的类必须再次验证null或为空,例如:

public class MyClass {

    public void MyMethod(string param){

        if(string.IsNullOrEmpty(param)){
            throw new ArgumentNullException(...);
        }
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

很明显,该方法的行为对于两个(无效)值都是相同的.这是一种非常常见的情况,在测试这些方法时,我总是怀疑如何做到这一点.我总是为这些案例创建两个单独的测试:

[TestClass]
public class Tests {

    [TestMethod]
    public void MyMethod_should_fail_if_param_is_null(){
        //...
        myclass.MyMethod(null);
        //...
    }

    [TestMethod]
    public void MyMethod_should_fail_if_param_is_empty(){
        //...
        myclass.MyMethod("");
        //...
    }
Run Code Online (Sandbox Code Playgroud)

}

但我看到太多的冗余.那些测试完全相同,唯一的区别是传递给方法的参数.这非常困扰我,因为我必须为每个字符串参数创建两个测试.具有3个参数的方法将仅具有6个测试以测试参数.

我认为这是测试这些参数的正确方法,但是如果我知道99%的字符串参数将以相同的方式进行验证,那么测试它们是否为null(或为空)并不是更好,并假设其中的行为另一种情况会是一样的吗?

我想知道你对此的看法.我知道我所要求的更多是技术意见而不是技术问题,但我认为测试社区可能会对这种情况说些有趣的话.

谢谢!

Jon*_*eet 12

就个人而言,我会考虑对所有参数使用单个测试.这不遵循单元测试的正常教条,但它增加了测试的可读性(通过最小化专用于非常重复的情况的测试代码的数量)并且没有太多的缺点.是的,如果测试失败,你不知道在第一次失败后的所有检查是否也会失败 - 但这在实践中真的是一个问题吗?

重要的是要确保你有一个捷径来测试案件.例如,你可能会写这样的东西(如果你的单元测试框架还没有):

public static void ExpectException<T>(Action action) where T : Exception
{
    try
    {
        action();
        Assert.Fail("Expected exception " + typeof(T).Name);
    }
    catch (T exception)
    {
        // Expected
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

[Test]
public void MyMethodFailsWithInvalidArguments()
{
    ExpectException<ArgumentNullException>(() => myClass.MyMethod(null));
    ExpectException<ArgumentException>(() => myClass.MyMethod(""));
}
Run Code Online (Sandbox Code Playgroud)

比使用单独的try/catch块,甚至使用ExpectedException属性和多个测试执行每个更简洁.

您可能希望在需要验证每种情况下都没有触及模拟对象(检查是否避免副作用)或可能会出现常见异常的情况时需要重载ArgumentNullException.

对于单参数方法,您甚至可以编写一个方法来封装您需要的内容:

public void ExpectExceptionForNullAndEmptyStrings(Action<string> action)
{
    ExpectException<ArgumentNullException>(() => action(null));
    ExpectException<ArgumentException>(() => action(""));
}
Run Code Online (Sandbox Code Playgroud)

然后用:

[Test]
public void MyMethodFailsWithInvalidArguments()
{
    // This *might* work without the 
    ExpectExceptionForNullAndEmptyStrings(myClass.MyMethod);
}
Run Code Online (Sandbox Code Playgroud)

...对于具有单个参数但非void返回类型的方法可能是另一个.

虽然可能有点远:)