Проблема с sys.argv [1], когда модуль unittest находится в скрипте

У меня есть сценарий, который выполняет различные действия и обращается к параметрам с помощью sys.argv, но когда сценарий достигает части кода unittest, он говорит, что для этого нет модуля. У меня есть сценарий:

class MyScript():

    def __init__(self):
        self.value = sys.argv[1]

    def hello(self):
        print self.value

    def suite(self):
        modules_to_test = ('external_sanity_onvif', 'starttest')
        alltests = unittest.TestSuite()
        for module in map(__import__, modules_to_test):
            alltests.addTest(unittest.findTestCases(module))
        return alltests


if __name__ == '__main__': 
    Run = MyScript()
    Run.hello()
    log_file = 'log_file.txt'
    test_file = open(log_file, "w") 
    runner = unittest.TextTestRunner(test_file)
    unittest.main(defaultTest='Run.suite', testRunner=runner)

Скажем, я ввожу ./script.py Hello в командную строку. Я получаю следующую ошибку:

AttributeError: 'module' object has no attribute 'Hello'

Если я удалю модуль unittest, он заработает. Также, если я удалю журнал testrunner и оставлю его по адресу:

unittest.main(defaultTest='Run.suite')

Это все еще не работает.

Кто-нибудь может помочь.

Спасибо

Я пробовал эту хитрость, но она все еще пытается прочитать sys.argv.

project = sys.argv[4:]
sys.argv = sys.argv[0:4]

Я пробовал это с argv, но читать лишние параметры все равно утомительно.


person chrisg    schedule 11.05.2010    source источник
comment
Не могли бы вы опубликовать полное сообщение об ошибке (с трассировкой стека)? Он сообщит нам, в какой строке произошел AttributeError.   -  person unutbu    schedule 11.05.2010


Ответы (4)


Проблема в том, что unittest.main() хочет, чтобы ваш драгоценный argv использовался в личных целях! Он использует либо аргумент argv, который вы даете ему в качестве параметра функции, либо sys.argv, если вы не указываете его явно, и пытается загрузить тесты с указанными вами аргументами. В данном случае это означает, что он ищет либо подмодуль с именем Hello, TestCase класс с именем Hello, метод тестового примера в классе тестового примера с именем Hello, либо вызываемый объект с именем Hello, который возвращает экземпляр TestCase или TestSuite, все в пределах вашего модуль 'скрипт'.

Есть несколько способов исправить это:

  • Обходите unittest.main() и самостоятельно вызывайте низкоуровневые функции unittest, чтобы настроить и запустить тестовые примеры, которые вы задумали.
  • Удалите зависимость вашего кода от sys.argv и используйте поведение unittest.main() в своих интересах. Если ваш модуль не предназначен для независимого запуска кроме в качестве модульного теста, это, вероятно, имеет смысл, поскольку вызывающие вашего модуля могут не ожидать, что вы прочитаете их argv!
  • Разделите тестовый код и основную процедуру в отдельный тестовый модуль. Вам все равно придется выяснить, как добавить правильный argv в свой код, хотя и из тестового модуля.
  • Укажите argv=[sys.argv[0]] в качестве аргумента unittest.main(); это должно удерживать его от попыток прочитать ваше.
person Owen S.    schedule 15.05.2010

Если вам не нужны функции командной строки модуля unittest, вы можете заставить модули optparse и unittest работать вместе, изменив sys.argv непосредственно перед вызовом unittest.main()

Попробуйте это прямо перед своим unittest.main() вызовом:

del sys.argv[1:]

Это удалит ваши аргументы командной строки до того, как unittest их увидит.

Если вы не используете модуль optparse, вы можете сделать это вместо этого:

my_args = sys.argv[1:]
del sys.argv[1:]
# do_stuff(my_args)
unittest.main()
person Will Pierce    schedule 13.08.2011

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

import sys
import unittest

class MyScript(unittest.TestCase):
    def setUp(self):
        self.value = "Default Value"

    def setHello(self, value):
        self.value = value

    def hello(self):
        return self.value

class UserA(MyScript):
    def setUp(self):
        self.setHello("UserA")

    def testMyName(self):
        self.failUnlessEqual(self.hello(), "UserA")

class UserB(MyScript):
    def setUp(self):
        self.setHello("UserB")

    def testMyName(self):
        self.failUnlessEqual(self.hello(), "UserB")

if __name__ == '__main__': 
    unittest.main()
person dash-tom-bang    schedule 11.05.2010

Python предоставляет вам всю возможную информацию, и если вы не хотите публиковать весь набор вещей, которые вы пытаетесь протестировать, мы можем только догадываться о том, чего вы пытаетесь достичь. Ошибка говорит, что нет атрибута с именем «Hello», поэтому, возможно, модули, которые вы пытаетесь протестировать, должны получить такую ​​переменную, функцию или что-то еще.

Есть ли причина, по которой вы просто не используете unittest.main()?

person dash-tom-bang    schedule 11.05.2010
comment
В этом конкретном фрагменте кода я пытаюсь передать Hello в качестве параметра, который будет работать. Затем у меня есть функция, которая вызывает серию тестов, которые не связаны с другими функциями. Но когда unittest и sys.argv находятся в одном скрипте, возникает проблема. Тесты, которые я выполняю, не имеют значения, поскольку они работают. Проблема в том, что когда я пытаюсь ввести параметр в функцию без ссылки, unittest, а затем запускаю функцию unittest ниже по файлу, я получаю сообщение об ошибке. Причина, по которой я использую этот тип команды unitest, заключается в том, что я вызываю определенный набор тестов. - person chrisg; 11.05.2010
comment
Ваши модульные тесты не должны принимать параметры из командной строки. Фреймворк unittest явно использует sys.argv для своих целей, хотя может быть способ переопределить это (например, может быть, есть параметр argv для вызовов unittest? Я не знаю, под рукой). Если вы хотите параметризовать свои тесты , так что вы можете создать их экземпляры с разными наборами данных, вам, вероятно, также следует сделать это с помощью фикстур. Я отправлю другой ответ. - person dash-tom-bang; 11.05.2010