Python: тестирование носа в проекте, содержащем базовые классы - PullRequest
0 голосов
/ 05 декабря 2018

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

Я новичок в Python, поэтому извиняюсь, если моя проблема заключается в чем-то тривиальном / рудиментарном.

В этом проекте используются популярныеПакет Python Finite State Machine переходы , от которого мой базовый класс наследует State (как видно из моего states.py).

После запуска $nosetests изнутри base_tester/, я получаю 3 повторных ошибки (только 1 вставлена ​​для краткости):

E.EE.
======================================================================
ERROR: Failure: TypeError (__init__() missing 1 required positional argument: 'name')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/user/.pyenv/versions/3.7.1/lib/python3.7/site-packages/nose/failure.py", line 39, in runTest
    raise self.exc_val.with_traceback(self.tb)
  File "/home/user/.pyenv/versions/3.7.1/lib/python3.7/site-packages/nose/loader.py", line 523, in makeTest
    return self._makeTest(obj, parent)
  File "/home/user/.pyenv/versions/3.7.1/lib/python3.7/site-packages/nose/loader.py", line 582, in _makeTest
    return MethodTestCase(obj)
  File "/home/user/.pyenv/versions/3.7.1/lib/python3.7/site-packages/nose/case.py", line 346, in __init__
    self.inst = self.cls()
  File "/home/user/repos/openrov/production/testers/base_tester/base_tester/states.py", line 6, in __init__
    super().__init__(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'name'

Однако , когда я создаю небольшой тестовый проект и реализую класс наследования, он работает какпредназначена.Мне остается верить, что что-то не так с тем, что я делаю в test.py, но не могу понять, что.

Спасибо!

Структура проекта:

base_tester/
├── base_tester/
│   ├── __init__.py
│   └── states.py
├── setup.py
└── test/
    ├── __init__.py
    └── test.py

states.py:

from transitions import State

# Parent class
class TestParent(State):    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    # To be implemented by inheriting classes
    def run_test(self):
        raise NotImplementedError()

base_tester / init .py:

from base_tester.states import *

test / test.py:

import unittest
from base_tester import TestParent

class Test1(TestParent):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def run_test(self):
        pass

class Tester(unittest.TestCase):
    test1 = Test1(name='test1')

    def test_1(self):
        self.assertTrue(True)

    def main(self):
        pass

if __name__ == '__main__':
    unittest.main()

1 Ответ

0 голосов
/ 07 декабря 2018

Проблема заключается в именовании методов в TestParent и Test1.Более конкретно, это Test1/TestParent.run_test, что вызывает проблемы.Я предполагаю, что nose считает эти методы изолированными тестами.В результате он пытается создать экземпляр рассматриваемого класса и терпит неудачу, так как TestParent, Test1 и State не имеют значения по умолчанию name.

См. Документы о поиске тестов..Здесь упоминается:

Если это похоже на тест, то это тест.Имена каталогов, модулей, классов и функций сравниваются с регулярным выражением testMatch, а те, которые соответствуют, считаются тестами.Любой класс, который является подклассом unittest.TestCase, также собирается, если он находится внутри модуля, который выглядит как тест.

Это также причина, по которой вы получаете сообщение об ошибке несколько раз.Ваш вывод предполагает, что вы (пытаетесь) выполнить пять тестов, из которых три не пройдены.Я получил только 4, но и три провальных теста.Чтобы проиллюстрировать открытие немного лучше, я добавил значение по умолчанию name к TestParent и пропустил TestParent.run_test.Если я выполню nosetests -v сейчас, я получу следующий вывод:

base_tester.TestParent.run_test ... ok
test.test.Test1.run_test ... ok
test.test.TestParent.run_test ... ok
test_1 (test.test.Tester) ... ok

TestParent.run_test выполняется дважды из-за импорта.Простым решением было бы избежать имен методов, которые соответствуют регулярному выражению тестового шаблона (упомянуто в документах ), которое по умолчанию равно (?:\b|_)[Tt]est.Иногда, однако, невозможно избежать наличия test в именах методов по семантическим причинам.

Обнаружение теста упоминает следующее:

Если объект определяет атрибут __test__, который не оценивается какПравда, этот объект не будет собираться, равно как и любые содержащиеся в нем объекты.

В вашем случае добавление __test__ = False к TestParent также должно решить проблему:

# Parent class
class TestParent(State):
    __test__ = False  # avoid nosetests test discover

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    # To be implemented by inheriting classes
    def run_test(self):
        raise NotImplementedError()
...