2013-12-03 3 views
1

Возможно, я прошу что-то совершенно очевидное. У меня нет большого опыта написания модульных тестов, и появился следующий вопрос, который заставил меня задуматься.Единичные тесты, можете ли вы протестировать один метод?

Скажите, у вас есть класс, и у этого класса есть методы, которые вы хотите протестировать. Но можете ли вы одновременно протестировать один метод? Думаю, нет. Чтобы протестировать метод, вам нужно вызвать один или несколько других методов. Например:

class MyClass { 
    int x; 
    void foo() { x = 4; } 
    boolean bar() { x = 3; } 
    boolean check() { return x == 4; } 
} 

Для того, чтобы проверить Foo и бар, мне нужно использовать чек(), а с другой стороны, для того, чтобы проверить проверить, что нужно использовать либо Foo() или бар().

Скажем, я должен следующего теста:

class MyClassTest { 
    @Test 
    void testFoo() { 
     MyClass obj = new MyClass(); 
     obj.foo(); 
     assert obj.check(); 
    } 
} 

Теперь давайте предположим, мой коллега изменяет метод проверки():

boolean check() { return x == 5; } 

Конечно, testFoo() потерпит неудачу, и можно подумать, что существует проблема с методом foo.

Итак, это похоже на ситуацию с куриным яйцом. Как люди обычно это разрешают?

+1

Что такое единица измерения? Обычно это область, в которой она применима к тесту ... иногда это так же аккуратно, как и один метод, но часто вам нужно создать «приспособление» или «набор», чтобы определить свою «единицу работы». –

ответ

2

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

  1. «check должен вернуться true когда foo был назван последним»
  2. «check должен вернуться false когда bar был назван последним»

Так ты будет писать два модульных теста, по одному для каждого требования, чтобы убедиться, что класс правильно его выполняет (может потребоваться сформулировать третье требование: то, что должно делать check, когда не было вызвано). Когда ваш коллега внес изменения выше, он не нарушил ни метода foo, ни метода check. Первое, что он нарушил, было первым. Теперь он должен каким-то образом изменить класс, чтобы он снова выполнял его. Как он это совершает (смена foo, смена check или обоих) не имеет значения.

+0

Вы действительно ответили на мой вопрос, указав, что я не должен тестировать методы, но тестировать поведение. –

5

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

class WhenFooWasCalled { 
    @Test 
    public void ThenCheckShouldReturnTrue { 
     MyClass sut = new MyClass(); 
     sut.foo(); 
     assertTrue(sut.check()); 
    } 
} 

class WhenBarWasCalled { 
    @Test 
    public void ThenCheckShouldReturnFalse { 
     MyClass sut = new MyClass(); 
     sut.bar(); 
     assertFalse(sut.check()); 
    } 
} 

class WhenNothingWasCalled { 
    @Test 
    public void ThenCheckShouldReturnFalse { 
     MyClass sut = new MyClass(); 
     assertFalse(sut.check()); 
    } 
} 

Обновлено: Я добавил третий тестовый случай: Как чек() ведут себя, если ни Foo(), ни бар() были называется.

+0

Это хорошая идея иметь эти тесты в двух разных классах? – siledh

+1

@siledh Это полностью зависит от вас и всего лишь элементарного стиля. Я предпочитаю подход типа BDD, который помогает мне читать имя класса + имя метода, например, предложение, например. * WhenFooWasCalled.ThenCheckShouldReturnTrue() *. –

+0

Итак, мое предположение ошибочно, что каждый метод должен иметь метод тестирования, в котором вы проверяете только этот метод? –

1

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

Смежные вопросы