Uruchom setUp tylko raz dla zestawu testów automatycznych

81

Moja wersja Pythona to 2.6.

Chciałbym wykonać test setUp tylko raz, ponieważ robię tam rzeczy potrzebne do wszystkich testów.

Mój pomysł polegał na utworzeniu zmiennej boolowskiej, która po pierwszym wykonaniu zostanie ustawiona na „true”, a następnie wyłączy więcej niż jedno wywołanie metody setup.

class mySelTest(unittest.TestCase):
    setup_done = False

    def setUp(self):
        print str(self.setup_done)
            
        if self.setup_done:
            return
        self.setup_done = True
        print str(self.setup_done)

Wyjście:

False

True

--- Test 1 ---

False

True

--- Test 2 ---

dlaczego to nie działa? Czy coś przegapiłem?

Kesandal
źródło
6
Unittest tworzy oddzielne instancje dla każdego testu
David Robinson
2
Nie rób tego. Zaimplementuj inny mechanizm. Ale nie próbuj zmieniać znaczenia setUp.
David Heffernan
Możliwy duplikat Unittest setUp / tearDown dla kilku testów
Stevoisiak

Odpowiedzi:

114

Możesz użyć setUpClassdo zdefiniowania metod, które działają tylko raz na zestaw testowy.

Daniel Roseman
źródło
Dzięki za odpowiedź. Ponieważ używam Pythona 2.6.6 setUpClass nie jest dostępny.
Kesandal
2
@JohnM .: Możesz pobrać pakiet backport unittest2 i pobrać wszystkie nowe rzeczy na starszą wersję Python-dist.
Macke,
Pytanie dotyczy Pythona 2, ale ponieważ odpowiedź dotyczy również Pythona 3, zmieniłem adres URL, ponieważ Python 2 jest przestarzały.
CharlesB
66

Daniela odpowiedź jest poprawna, ale tutaj jest przykład uniknąć pewnych typowych błędów znalazłem, takie jak niestawienie super()się setUpClass()gdy TestCasejest podklasą unittest.TestCase(jak w django.testlub falcon.testing).

Dokumentacja dla setUpClass()nie wspomina, że super()w takich przypadkach należy dzwonić . Jeśli tego nie zrobisz, pojawi się błąd, jak widać w tym powiązanym pytaniu .

class SomeTest(TestCase):
    def setUp(self):
        self.user1 = UserProfile.objects.create_user(resource=SomeTest.the_resource)

    @classmethod
    def setUpClass(cls):
        """ get_some_resource() is slow, to avoid calling it for each test use setUpClass()
            and store the result as class variable
        """
        super(SomeTest, cls).setUpClass()
        cls.the_resource = get_some_resource()
Chemary
źródło
1
Zauważ, że ma to znaczenie tylko wtedy, gdy TestCasejest to podklasa unittest.TestCase.
EliadL
3

Jeśli trafiłeś tutaj z powodu konieczności załadowania danych do testów ... to jeśli używasz Django 1.9+, przejdź do setUpTestData :

class MyTests(TestCase):

    @classmethod
    def setUpTestData(cls):
        # Set up data for the whole TestCase
        cls.foo = Foo.objects.create(bar="Test")

    def test1(self):
        self.assertEqual(self.foo.bar, 'Test') 
andilabs
źródło
2

Nie próbuj deduplikować wywołań setUp, po prostu wywołaj to raz.

Na przykład:

class MyClass(object):
    ...

def _set_up():
    code to do one-time setup

_set_up()

Spowoduje to wywołanie funkcji _set_up () przy pierwszym załadowaniu modułu. Zdefiniowałem ją jako funkcję na poziomie modułu, ale równie dobrze możesz uczynić ją metodą klasową MyClass.

Paul Hankin
źródło
2

Umieść cały kod, który chcesz skonfigurować, poza mySelTest.

setup_done = False

class mySelTest(unittest.TestCase):

    def setUp(self):
        print str(setup_done)

        if setup_done:
            return

        setup_done = True
        print str(setup_done)

Inną możliwością jest posiadanie klasy Singleton, w której tworzysz instancję setUp(), która uruchomi __new__kod tylko raz i zwróci instancję obiektu na resztę wywołań. Zobacz: Czy istnieje prosty, elegancki sposób definiowania singletonów?

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                            cls, *args, **kwargs)
            # PUT YOUR SETUP ONCE CODE HERE!
            cls.setUpBool = True

        return cls._instance

class mySelTest(unittest.TestCase):

    def setUp(self):
        # The first call initializes singleton, ever additional call returns the instantiated reference.
        print(Singleton().setUpBool)

Twój sposób też działa.

NuclearPeon
źródło
2

setup_done to zmienna klasy, a nie zmienna instancji.

Odwołujesz się do niej jako do zmiennej instancji:

self.setup_done

Ale musisz odwołać się do niej jako zmiennej klasy:

mySelTest.setup_done

Oto poprawiony kod:

class mySelTest(unittest.TestCase):
    setup_done = False

    def setUp(self):
        print str(mySelTest.setup_done)

        if mySelTest.setup_done:
            return
        mySelTest.setup_done = True
        print str(mySelTest.setup_done)
fasola jersey
źródło
2

Używam Pythona 3 i stwierdziłem, że clsodwołanie jest również dostępne w setupmetodzie, więc działa to:

class TestThing(unittest.TestCase):

  @classmethod
  def setUpClass(cls):
    cls.thing = Thing() # the `thing` is only instantiated once

  def setup(self):
    self.thing = cls.thing # ...but set on each test case instance

  def test_the_thing(self):
    self.assertTrue(self.thing is not None)
Greg Ross
źródło
1
Nie możesz uzyskać dostępu do cls w ramach metody setUp, która jest metodą instancji (małe litery setup () nie zostaną wywołane, ponieważ nie jest to metoda unittest setUp ani metoda zaczynająca się od 'test', prawdopodobnie dlatego nie otrzymałeś błąd) Jednak po zdefiniowaniu zmiennych klasy w setUpClass, możesz po prostu wywołać self.thing we wszystkich metodach instancji
khuang834
Dzięki, @ khuang834. Masz rację. To było przeoczenie z mojej strony.
Greg Ross,