Сохранять изменения переменных между тестами в unittest?

Как сохранить изменения, сделанные в одном и том же объекте, наследующем от TestCase в unitttest?

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):
    foo = 'bar'

    def setUp(self):
        pass

    def test_a(self):
        self.assertEqual(self.foo, 'bar')
        self.foo = 'can'

    def test_f(self):
        self.assertEqual(self.foo, 'can')


if __name__ == '__main__':
    unittest_main()

То есть: я хочу, чтобы эти два теста выше прошли


person A T    schedule 30.01.2014    source источник
comment
Модульные тесты должны быть независимыми. [В любом случае self.foo относится к переменной экземпляра, а foo = 'bar' (где он расположен) назначает переменную класса.]   -  person user2864740    schedule 30.01.2014
comment
Я тестирую OAuth2; login устанавливает access_token, который мне нужен для следующих нескольких тестов.   -  person A T    schedule 30.01.2014
comment
Разве вы не можете создать другой тестовый класс с соответствующим setUp, чтобы имитировать соответствующий access_token? (Заставьте setUp вызывать OAuth по мере необходимости; на этом этапе больше интеграционного тестирования.. но примите во внимание, что setUp не может быть неправильным, иначе другой TestCase потерпит неудачу.)   -  person user2864740    schedule 30.01.2014
comment
См. stackoverflow.com/questions/3843171/ - на самом деле не ответы, а предложения и ссылки.   -  person user2864740    schedule 30.01.2014
comment
Попробуйте изучить setUpClass(). Он запускается один раз, когда вызывается ваш класс. Вы можете использовать его для настройки переменных, необходимых для нескольких тестов.   -  person Stevoisiak    schedule 06.11.2017


Ответы (3)


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

Подобно тому, что говорил @Matthias, но я бы сделал одну вещь по-другому для случаев, когда вы можете решить наследовать от класса позже.

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):
    foo = 'bar'

    def setUp(self):
        pass

    def test_a(self):
        self.assertEqual(self.__class__.foo, 'bar')
        self.__class__.foo = 'can'

    def test_f(self):
        self.assertEqual(self.__class__.foo, 'can')


if __name__ == '__main__':
    unittest_main()

Разница между этим ответом и ответом @Matthias, который вы приняли, заключается в явном объявлении класса по сравнению с поиском указанной ссылки на класс.

TestSimpleFoo vs self.__class__

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

person rdp    schedule 24.07.2014
comment
Да, я использовал весь синтаксис def test_0, чтобы преодолеть проблемы с сортировкой. Конечно, буду использовать __class__; что также более лаконично =). Да, я мог бы перестроить свои тесты; но предпочел бы более чистый тестовый интерфейс. - person A T; 25.07.2014

Мне нравится ваш собственный ответ за его простоту, но если вы хотите сохранить отдельные модульные тесты:

Судя по всему, unittest запускает отдельные тесты со свежими экземплярами TestCase. Ну, просто привяжите сохраняемые объекты к чему-то другому, кроме себя. Например:

from unittest import TestCase, main as unittest_main


class TestSimpleFoo(TestCase):

    def setUp(self):
        pass

    def test_a(self):
        TestSimpleFoo.foo = 'can'

    def test_f(self):
        self.assertEqual(TestSimpleFoo.foo, 'can')


if __name__ == '__main__':
    unittest_main()

Возможно, вас также заинтересуют setUpClass и tearDownClass: https://docs.python.org/3/library/unittest.html#setupclass-and-teardownclass

Также позаботьтесь о порядке выполнения модульных тестов: https://docs.python.org/2/library/unittest.html#unittest.TestLoader.sortTestMethodsUsing

person Matthias    schedule 18.07.2014
comment
Спасибо, я уже использую @tearDownClass для своей функции unregister. Интересный подход, который вы предлагаете: попробуем. - person A T; 18.07.2014
comment
Ты не должен этого делать. Что, если ваш исполнитель тестов решит запускать тесты в отдельных процессах (например, для распределения нагрузки на несколько компьютеров)? Есть определенные предположения, которые тестировщики делают о модульном тесте, и вы не должны их нарушать. Как говорит Нед, ответ AT — правильный способ сделать это. - person Burak Arslan; 22.07.2014
comment
Мне не нравится это решение, нет необходимости структурировать ваши тесты таким образом. Я думаю, что наиболее жизнеспособным решением является реструктуризация/разработка ваших тестов, чтобы они соответствовали концептуальным идеям модульного тестирования Python. - person brunsgaard; 23.07.2014
comment
@brunsgaard Я могу понять. Это обходной путь и как таковой не очень красивый. Основная проблема заключается в том, что модульное тестирование Python не поддерживает концепцию тестовых последовательностей. Тестовые последовательности, конечно, являются лишь вторым лучшим решением после независимых модульных тестов, но перед лицом компромисса между временем выполнения теста и удобством сопровождения теста это правильное решение. - person Matthias; 24.07.2014
comment
@Matthias, вы можете взглянуть на pytest.org, я этого не знаю, но люди говорят об этом здесь, в EuroPython. Может быть, это пойдет вам на пользу :) Вчерашний разговор youtube.com/watch?v=LdVJj65ikRY - person brunsgaard; 24.07.2014
comment
@brunsgaard Спасибо, совместное использование фикстуры в тестах в модуле — это именно то, что нужно OP: pytest.org/latest/ - person Matthias; 25.07.2014

Не мог понять это; поэтому в итоге взломал его с помощью нескольких функций без префикса test_:

def test_password_credentials_grant(self):
    for user in self.user_mocks:
        self.register(user)
        self.login(user)
        self.access_token(user, self.assertEqual)  # Ensures access_token is generated+valid
        self.logout(user)
        self.access_token(user, self.assertNotEqual)  # Ensures access_token is now invalid
        self.unregister(user)
person A T    schedule 30.01.2014