Модульность Python Unittest против удобочитаемости - PullRequest
7 голосов
/ 14 февраля 2012

У меня есть тестовый модуль Python, в некоторых тестах проверяется объект одного типа. Основная схема в одном тестовом классе:

class TestClass(unittest.TestCase):
    def setup(self):
        ...

    def checkObjects(self, obj):
        for i in [...values...]:
            self.assertEqual(starttags(i,obj), endtags(i,obj))

    def testOne(self):
        #Get object one.
        checkObjects(objone)

    def testAnother(self):
        #Access another object.
        checkObjects(another)

    ... various tests for similar objects.

Несмотря на то, что он модульный, я заметил, что любые сбои приводят к ошибке, такой как AssertionError: number! = Anothernumber и строка кода, генерирующая ошибку, self.assertEqual(starttags(i,obj), endtags(i,obj)). Если бы я перечислял тесты вместо того, чтобы помещать их в цикл for, у меня было бы что-то вроде:

self.assertEqual(starttags(value1,obj), endtags(value1,obj))
self.assertEqual(starttags(value2,obj), endtags(value2,obj))

Который точно указывает, в каком случае возникла ошибка, но представляет собой код копирования и вставки, который, как я думал, обычно не рекомендуется. Недавно я заметил проблему, когда участник переработал более clean unit-test, который, к сожалению, дал бы мало отладочной информации при сбое утверждения. Итак, какова лучшая практика в этих случаях? Что-то вроде списка кортежей, передаваемых в цикл for с помощью assertEquals, «чище», но копирование-вставка с различными значениями в разных строках дает полезные трассировки стека.

Ответы [ 2 ]

7 голосов
/ 14 февраля 2012

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

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

Создайте свои тесты (код) с учетом простоты

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

Создайте свои тесты таким образом, чтобы, когда один из них сломался, вы сразу знали , почему он это сделал и где. Мало того, разработайте их таким образом, чтобы вы могли в мгновение ока сказать, что они делают. Наличие для циклов , ifs и в принципе любого другого вида механизма управления потоком (или слишком обширного рефакторинга) в тестовом коде, как правило, приводит к тому, что один вопрос возникает у вас. ум - Что мы здесь делаем? Это вопрос, который вы не хотите задавать себе.

Подводя итог этого длинного поста словами людей умнее меня:

Любой дурак может написать код, понятный компьютеру. Хорошие программисты пишут код, понятный людям.

- Мартин Фаулер и др., Рефакторинг: улучшение дизайна существующего кода, 1999

Сделайте себе одолжение и придерживайтесь этого правила.

0 голосов
/ 14 февраля 2012

Используйте тесты носа, это делает ваши тесты намного чище:

#!/usr/bin/python
def test_one():
    for i in [...]:
        assert xxx(i) == yyy

def test_two():
    ...
...