Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

как автоматически создавать TestSuite?

#python #юнит_тесты #test_driven_development


Наверное тяжело будет тем кто не читал TDD Кента Бека, но думаю многие читали, поэтому
прошу помощи. Уже 2 дня бьюсь, с ног сбился. В конце части 2 там получилась вот такая
архитектура:

    class TestCase:
     def __init__(self, name):
        self.name = name
     def setUp(self):
        self.result = TestResult()
     def run(self, result):
        result.testStarted()
        self.setUp()
        try:
           method = getattr(self, self.name)
           method()
        except:
           result.testFailed()
        self.tearDown()
     def tearDown(self):
        pass

class WasRun(TestCase):
    def __init__(self, name):
        TestCase.__init__(self, name)
    def testMethod(self):
        self.wasRun=1
        self.log = self.log+"testMethod "
    def setUp(self):
        self.wasRun = None
        self.log = "setUp "
    def tearDown(self):
        self.log = self.log + "tearDown "
    def testBrokenMethod(self):
        raise Exception

class TestResult:
    def __init__(self):
        self.runCount = 0
        self.errorCount = 0
    def testStarted(self):
        self.runCount = self.runCount + 1
    def testFailed(self):
        self.errorCount = self.errorCount + 1
    def summary(self):
        return "%d run, %d failed" % (self.runCount, self.errorCount)

class TestSuite:
    def __init__(self):
        self.tests = []
    def add(self, test):
        self.tests.append(test)
    def run(self, result):
        for test in self.tests:
           test.run(result)       

class TestCaseTest(TestCase):
    def testTemplateMethod(self):
        test = WasRun("testMethod")
        test.run(self.result)
        assert("setUp testMethod tearDown " == test.log)
    def testResult(self):
        test = WasRun("testMethod")
        result = test.run(self.result)
        assert("1 run, 0 failed" == self.result.summary())
    def testFailedResult(self):
        test = WasRun("testBrokenMethod")
        result = test.run(self.result)
        assert("1 run, 1 failed" == self.result.summary())
    def testFailedResultFormatting(self):
        result = TestResult()
        result.testStarted()
        result.testFailed()
        assert("1 run, 1 failed" == self.result.summary())
    def testSuite(self):
        suite = TestSuite()
        suite.add(WasRun("testMethod"))
        suite.add(WasRun("testBrokenMethod"))
        result = TestResult()
        suite.run(self.result)
        assert("2 run, 1 failed" == self.result.summary())


Далее мы создаем тестовый набор, вот так:

suite = TestSuite()
result = TestResult()
suite.add(TestCaseTest("testTemplateMethod"))
suite.add(TestCaseTest("testResult"))
suite.add(TestCaseTest("testFailedResult"))
suite.add(TestCaseTest("testFailedResultFormatting"))
suite.add(TestCaseTest("testSuite"))
result = TestResult()
suite.run(result)
print result.summary()


И дальше Кент Бек пишет "Создать объект TestSuite автоматически на основе TestCase"

Не совсем понимаю, что это значит, подскажите пожалуйста. Я так понял: вместо тех
10 строк что я привел во втором листинге, тест должен запускаться одной:

TestCaseTest().run()


И после этой строки все тестовые методы, которые есть в тестовом классе должны выполняться.
Для этого нам нужен конструктор без параметров и метода run() без параметров. Но
у нас уже есть конструктор и метод run() с параметрами, то есть нужна перегрузка, но
перегрузки нет в Python. Значит нужно создать версии этих методов с параметрами по
умолчанию. Хотя бы правильная идея?
    


Ответы

Ответ 1



Оригинальная цитата из книги: There is substantial duplication here, which we could eliminate if we had a way of constructing a suite automatically given a test class. Да, под here понимаются как раз строчки suite.add(...). Согласитесь - ручками каждый метод добавлять не самая удачная идея, может быть их там 100. Т.е. подразумевается что хотелось бы реализовать функционал, который соберет TestSuite из методов TestCaseTest автоматически сам, без нашего участия. Реализовать это можно различными способами, думаю самый простой: С помощью dir() получить методы TestCaseTest и отфильтровать те из них, что начинаются с test* Создать TestSuite Пройтись в цикле по списку методов из пункта 1 и добавить их в TestSuite при помощи suite.add(TestCaseTest("имя_метода")) Теперь уже выполнить suite.run(result) на собранном TestSuite Облечь эту логику можно, например, в отдельный класс TestLoader. Вот на github товарищ решал эту задачу: https://github.com/lenin/TDDbyExample/blob/7275d259132b44a5f24b116b4f61e47e421d7d35/PyTesting/tests.py и https://github.com/lenin/TDDbyExample/blob/7275d259132b44a5f24b116b4f61e47e421d7d35/PyTesting/testing.py получилось довольно аккуратно на мой взгляд. P.S.: Интересная книжка, спасибо за наводку ;)

Ответ 2



Если Ваша цель упростить запуск тестов, то на этот случай моя рекомендация поставить пакет: pip install nose и использовать команду запуска: nosetests -v.

Комментариев нет:

Отправить комментарий