Python: тесты носа для проекта, содержащего базовые классы

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

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

В этом проекте используется популярный пакет Python Finite State Machine переходы, от которого мой базовый класс наследует Состояние (как показано в моем 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 / в этом.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()
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
42
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

If it looks like a test, it’s a test. Names of directories, modules, classes and functions are compared against the testMatch regular expression, and those that match are considered tests. Any class that is a unittest.TestCase subclass is also collected, so long as it is inside of a module that looks like a test.

Это также причина, по которой вы получаете сообщение об ошибке несколько раз. Ваш вывод предполагает, что вы (пытаетесь) запустить пять тестов, из которых три не пройдут. Я получил только 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 в именах методов по семантическим причинам.

В открытии теста упоминается следующее:

If an object defines a __test__ attribute that does not evaluate to True, that object will not be collected, nor will any objects it contains.

В вашем случае добавление __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()

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

laughlinb 10.12.2018 20:12

Другие вопросы по теме