Как я могу украсить метод unittest Python, чтобы пропустить, если свойство, которое я ранее оценил, не является True?

Я пишу TestCase в Python, используя unittest, который выглядит примерно так:

class MyTestCase(unittest.TestCase):
  def setUp(self):
    # ... check if I'm online - might result in True or False
    self.isOnline = True

  @unittest.skipIf(not self.isOnline, "Not online")
  def test_xyz(self):
    # do a test that relies on being online

Однако это не работает, я полагаю, потому что @skipIf не может использовать self вне тела объявления функции. Я знаю, что могу проверить значение self.isOnline внутри функции test_xyz и использовать skipTest вместо этого, но это менее элегантно. Есть ли другие варианты?

Ответ 1

Вы можете написать свой собственный декоратор, которому вы передадите имя флага:

def skipIfTrue(flag):
    def deco(f):
        def wrapper(self, *args, **kwargs):
            if getattr(self, flag):
                self.skipTest()
            else:
                f(self, *args, **kwargs)
        return wrapper
    return deco

Затем в вашем классе вы определяете метод теста следующим образом:

@skipIfTrue('isOnline')
def test_thing(self):
    print("A test")

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

Ответ 2

Если вы можете переместить тест isOnline вне вашего метода setUp, то это решение:

IS_ONLINE = i_am_online()

class MyTestCase(unittest.TestCase):
  @unittest.skipUnless(IS_ONLINE, "Not online")
  def test_xyz(self):
    # do a test that relies on being online

Другой (более элегантный вариант):

import unittest

def skipWhenOffline():
    if not i_am_online():
         return unittest.skip("Not online")
    return unittest._id

class MyTestCase(unittest.TestCase):
  @unittest.skipWhenOffline()
  def test_xyz(self):
    # do a test that relies on being online

Однако, если это невозможно для вас, тогда нет более элегантного решения, чем каким-то образом использовать skipTest().

skipIf и skipUnless оцениваются во время объявления класса (они устанавливают атрибут __unittest_skip__ в вашем методе, чтобы указать, что он не должен запускаться, который позже просматривается до запуска теста). Ваш метод setUp просто еще не запущен в этой точке.

Ответ 3

Я бы добавил к ответу BrenBarn, что если вы используете nosetests, вам нужно украсить функцию wrapper @wraps. Без этого тестовая функция технически будет wrapper, и это не будет вызываться nosetests, так как по умолчанию методы тестирования должны начинаться с test_.

from unittest2.compatibility import wraps

def skipIfTrue(flag):
    def deco(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            if getattr(self, flag):
                self.skipTest()
            else:
                f(self, *args, **kwargs)
        return wrapper
    return deco

Ответ 4

Все остальные ответы здесь могут работать, но они более или менее взломаны и/или нуждаются в сторонних решениях.

Можно также пойти по старому пути, просто поставив if condition: self.skipTest() в качестве первой строки в вашей тестовой функции.

def test_xyz(self):
    if not self.isOnline:
        self.skipTest("Not online")
    ## You could even write it as a one-liner, should you prefer
    # if not self.isOnline: self.skipTest("Not online")
    ## Which happens to be exact the same length as the decorator,
    ## so it is roughtly the same as decorator even from the convenience standpoint
    # @unittest.skipIf(not self.isOnline, "Not online")
    do_a_test_that_relies_on_being_online(...)