Как мне проверить нулевые и / или пустые строковые параметры в методе? - PullRequest
9 голосов
/ 31 мая 2009

Обычно существуют классы с методами со строковыми параметрами, которые должны быть проверены на пустое или пустое значение, как в этом примере:

public class MyClass {

    public void MyMethod(string param){

        if(string.IsNullOrEmpty(param)){
            throw new ArgumentNullException(...);
        }
        //...
    }
}

Понятно, что поведение метода одинаково для обоих (недопустимых) значений. Это очень распространенная ситуация, и когда дело доходит до тестирования этих методов, я всегда сомневаюсь, как это сделать. Я всегда создаю два отдельных теста для этих случаев:

[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("");
        //...
    }

}

Но я вижу слишком много избыточности. Эти тесты точно такие же, с той лишь разницей, что параметр передается методу. Это очень беспокоит меня, так как я должен создать два теста для каждого строкового параметра. Метод с 3 параметрами будет иметь 6 тестов только для проверки параметров.

Я думаю, что это правильный способ проверки этих параметров, но если я знаю, что 99% строковых параметров будут проверяться одинаково, не лучше ли просто проверить их на нулевое (или пустое) значение и предположить, что поведение в другом случае будет таким же?

Я хотел бы знать, что вы думаете об этом. Я знаю, что спрашиваю, скорее, техническое мнение, чем технический вопрос, но я думаю, что сообщество тестировщиков может сказать что-то интересное об этой ситуации.

Спасибо!

Ответы [ 2 ]

12 голосов
/ 31 мая 2009

Лично я бы рассмотрел , используя один тест для всех параметров. Это не следует обычной догме модульного тестирования, но повышает читаемость тестов (сводя к минимуму объем тестового кода, который посвящен довольно повторяющемуся случаю) и не имеет большого количества недостатков. Да, если тест не пройден, вы не знаете, все ли проверки после первого неудачного теста также не пройдут - но действительно ли это проблема на практике?

Важно убедиться, что у вас есть короткий путь для тестирования кейса. Например, вы можете написать что-то вроде этого (если у вашего фреймворка пока нет этого):

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

Тогда вы можете написать:

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

Гораздо лаконичнее, чем выполнение каждого с отдельным блоком try / catch или даже использование атрибута ExpectedException и нескольких тестов.

Возможно, вы захотите перегрузки для случаев, когда вы также хотите проверить, что в каждом случае не было прикосновений к поддельным объектам (чтобы избежать побочных эффектов), или, возможно, перегрузки для распространенных исключений, таких как ArgumentNullException.

Для однопараметрических методов вы могли бы даже написать метод для инкапсуляции именно того, что вам нужно:

public void ExpectExceptionForNullAndEmptyStrings(Action<string> action)
{
    ExpectException<ArgumentNullException>(() => action(null));
    ExpectException<ArgumentException>(() => action(""));
}

затем позвоните с:

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

... и, возможно, еще один для методов с одним параметром, но не возвращаемым типом возврата.

Это, возможно, немного далеко, хотя:)

0 голосов
/ 31 мая 2009

Если вы используете Java и JUnit, вы можете использовать этот синтаксис

@Test(expected = IllegalArgumentException.class)
public void ArgumentTest() {
   myClass.MyMethod("");
   myClass.MyMethod(null);
}
...