Из документов Java 5 (Java 8 docs здесь ):
Когда объект MathContext поставляется с настройкой точности 0 (например, MathContext.UNLIMITED), арифметические операции точны, а также арифметические методы, которые не принимают объект MathContext. (Это единственное поведение, которое поддерживалось в версиях до 5.)
. В качестве следствия вычисления точного результата настройка режима округления объекта MathContext с точностью 0 не используется и таким образом, не имеет значения. В случае деления точное частное может иметь бесконечно длинное десятичное разложение; например, 1 делится на 3.
Если фактор имеет бесконечное десятичное расширение и операция указана для возврата точного результата, генерируется исключение ArithmeticException. В противном случае возвращается точный результат деления, как это сделано для других операций.
Чтобы исправить, вам нужно сделать что-то вроде этого:
a.divide(b, 2, RoundingMode.HALF_UP) where 2 is precision and RoundingMode.HALF_UP is rounding mode
Подробнее подробности: http://jaydeepm.wordpress.com/2009/06/04/bigdecimal-and-non-terminating-decimal-expansion-error/
Я использую что-то вроде этого:
import unittest
l = [["foo", "a", "a",], ["bar", "a", "b"], ["lee", "b", "b"]]
class TestSequense(unittest.TestCase):
pass
def test_generator(a, b):
def test(self):
self.assertEqual(a,b)
return test
if __name__ == '__main__':
for t in l:
test_name = 'test_%s' % t[0]
test = test_generator(t[1], t[2])
setattr(TestSequense, test_name, test)
unittest.main()
Пакет nose-parameterized
может использоваться для автоматизации этого процесса:
from nose_parameterized import parameterized
class TestSequence(unittest.TestCase):
@parameterized.expand([
["foo", "a", "a",],
["bar", "a", "b"],
["lee", "b", "b"],
])
def test_sequence(self, name, a, b):
self.assertEqual(a,b)
Какой будет генерировать тесты:
test_sequence_0_foo (__main__.TestSequence) ... ok
test_sequence_1_bar (__main__.TestSequence) ... FAIL
test_sequence_2_lee (__main__.TestSequence) ... ok
======================================================================
FAIL: test_sequence_1_bar (__main__.TestSequence)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/nose_parameterized/parameterized.py", line 233, in <lambda>
standalone_func = lambda *a: func(*(a + p.args), **p.kwargs)
File "x.py", line 12, in test_sequence
self.assertEqual(a,b)
AssertionError: 'a' != 'b'
Используйте библиотеку ddt . Он добавляет простые декораторы для методов тестирования:
import unittest
from ddt import ddt, data
from mycode import larger_than_two
@ddt
class FooTestCase(unittest.TestCase):
@data(3, 4, 12, 23)
def test_larger_than_two(self, value):
self.assertTrue(larger_than_two(value))
@data(1, -3, 2, 0)
def test_not_larger_than_two(self, value):
self.assertFalse(larger_than_two(value))
Эта библиотека может быть установлена с помощью pip
. Он не требует nose
и отлично работает с модулем стандартной библиотеки unittest
.
import unittest
def generator(test_class, a, b):
def test(self):
self.assertEqual(a, b)
return test
def add_test_methods(test_class):
#First element of list is variable "a", then variable "b", then name of test case that will be used as suffix.
test_list = [[2,3, 'one'], [5,5, 'two'], [0,0, 'three']]
for case in test_list:
test = generator(test_class, case[0], case[1])
setattr(test_class, "test_%s" % case[2], test)
class TestAuto(unittest.TestCase):
def setUp(self):
print 'Setup'
pass
def tearDown(self):
print 'TearDown'
pass
_add_test_methods(TestAuto) # It's better to start with underscore so it is not detected as a test itself
if __name__ == '__main__':
unittest.main(verbosity=1)
РЕЗУЛЬТАТ:
>>>
Setup
FTearDown
Setup
TearDown
.Setup
TearDown
.
======================================================================
FAIL: test_one (__main__.TestAuto)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:/inchowar/Desktop/PyTrash/test_auto_3.py", line 5, in test
self.assertEqual(a, b)
AssertionError: 2 != 3
----------------------------------------------------------------------
Ran 3 tests in 0.019s
FAILED (failures=1)
def add_test_methods
. Должно быть def _add_test_methods
, я думаю
– Raychaser
13 January 2017 в 20:12
Вам будет полезно попробовать библиотеку TestScenarios .
testscenarios обеспечивает чистую инъекцию зависимостей для тестов стиля стилей python. Это может быть использовано для тестирования интерфейса (тестирование многих реализаций через один набор тестов) или для классической инъекции зависимостей (обеспечить тестирование зависимостями внешне от самого тестового кода, что позволяет легко тестировать в разных ситуациях).
Использование unittest (начиная с 3.4)
Поскольку Python 3.4, стандартный библиотечный пакет unittest
имеет контекстный менеджер subTest
.
См. документацию:
Пример:
from unittest import TestCase
param_list = [('a', 'a'), ('a', 'b'), ('b', 'b')]
class TestDemonstrateSubtest(TestCase):
def test_works_as_expected(self):
for p1, p2 in param_list:
with self.subTest():
self.assertEqual(p1, p2)
Вы также можете укажите настраиваемое сообщение и значения параметра в subTest()
:
with self.subTest(msg="Checking if p1 equals p2", p1=p1, p2=p2):
Использование носа
Рамка носа носа поддерживает этот .
Пример (приведенный ниже код содержит все содержимое файла, содержащего тест):
param_list = [('a', 'a'), ('a', 'b'), ('b', 'b')]
def test_generator():
for params in param_list:
yield check_em, params[0], params[1]
def check_em(a, b):
assert a == b
Выход команды nosetests:
> nosetests -v
testgen.test_generator('a', 'a') ... ok
testgen.test_generator('a', 'b') ... FAIL
testgen.test_generator('b', 'b') ... ok
======================================================================
FAIL: testgen.test_generator('a', 'b')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.5/site-packages/nose-0.10.1-py2.5.egg/nose/case.py", line 203, in runTest
self.test(*self.arg)
File "testgen.py", line 7, in check_em
assert a == b
AssertionError
----------------------------------------------------------------------
Ran 3 tests in 0.006s
FAILED (failures=1)
У меня возникли проблемы с особым стилем параметризованных тестов. Все наши тесты Selenium могут выполняться локально, но они также должны быть удалены удаленно на нескольких платформах на SauceLabs. В принципе, я хотел взять большое количество уже написанных тестовых примеров и параметризовать их с минимальными изменениями кода. Кроме того, мне нужно было передать параметры в метод setUp, то, что я не видел в других решениях.
Вот что я придумал:
import inspect
import types
test_platforms = [
{'browserName': "internet explorer", 'platform': "Windows 7", 'version': "10.0"},
{'browserName': "internet explorer", 'platform': "Windows 7", 'version': "11.0"},
{'browserName': "firefox", 'platform': "Linux", 'version': "43.0"},
]
def sauce_labs():
def wrapper(cls):
return test_on_platforms(cls)
return wrapper
def test_on_platforms(base_class):
for name, function in inspect.getmembers(base_class, inspect.isfunction):
if name.startswith('test_'):
for platform in test_platforms:
new_name = '_'.join(list([name, ''.join(platform['browserName'].title().split()), platform['version']]))
new_function = types.FunctionType(function.__code__, function.__globals__, new_name,
function.__defaults__, function.__closure__)
setattr(new_function, 'platform', platform)
setattr(base_class, new_name, new_function)
delattr(base_class, name)
return base_class
При этом все, что мне нужно было сделать, это добавить простой декоратор @sauce_labs () в каждый обычный старый TestCase, а теперь, когда его запускают, они завернуты и переписаны, так что все методы тестирования параметризуются и переименованы. LoginTests.test_login (self) работает как LoginTests.test_login_internet_explorer_10.0 (self), LoginTests.test_login_internet_explorer_11.0 (self) и LoginTests.test_login_firefox_43.0 (self), и каждый из них имеет параметр self.platform, чтобы решить, какой браузер / платформу для запуска, даже в LoginTests.setUp, что имеет решающее значение для моей задачи, поскольку именно там инициализируется соединение с SauceLabs.
В любом случае, я надеюсь, что это может помочь кому-то, кто хочет сделать аналогичную «глобальная» параметризация их тестов!
Вы можете использовать TestSuite
и пользовательские классы TestCase
.
import unittest
class CustomTest(unittest.TestCase):
def __init__(self, name, a, b):
super().__init__()
self.name = name
self.a = a
self.b = b
def runTest(self):
print("test", self.name)
self.assertEqual(self.a, self.b)
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(CustomTest("Foo", 1337, 1337))
suite.addTest(CustomTest("Bar", 0xDEAD, 0xC0DE))
unittest.TextTestRunner().run(suite)
__init__
.
– jadelord
2 August 2017 в 19:42
Начиная с Python 3.4 для этой цели были введены подтесты для unittest. Подробнее см. В документации . TestCase.subTest - это менеджер контекста, который позволяет изолировать утверждения в тесте, чтобы сообщение об ошибке сообщалось с информацией о параметрах, но не останавливает выполнение теста. Вот пример из документации:
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
Выход тестового прогона будет:
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
Это также часть unittest2 , поэтому он доступен для более ранних версий Python.
setUp()
и tearDown()
не выполняются между субтестами.)
– Kevin Christopher Henry
9 April 2016 в 15:16
self.setUp()
можно теоретически вызывать вручную изнутри подтеста. Что касается tearDown
, то может быть достаточно, чтобы он автоматически вызывал в конце.
– A-B-B
1 May 2017 в 20:33
Это можно решить элегантно, используя метаклассы:
import unittest
l = [["foo", "a", "a",], ["bar", "a", "b"], ["lee", "b", "b"]]
class TestSequenceMeta(type):
def __new__(mcs, name, bases, dict):
def gen_test(a, b):
def test(self):
self.assertEqual(a, b)
return test
for tname, a, b in l:
test_name = "test_%s" % tname
dict[test_name] = gen_test(a,b)
return type.__new__(mcs, name, bases, dict)
class TestSequence(unittest.TestCase):
__metaclass__ = TestSequenceMeta
if __name__ == '__main__':
unittest.main()
__new__
из класса TestSequence
(и тем самым вообще пропустить метакласс)?
– petroslamb
18 January 2016 в 14:50
__new__
в метаклассе вызывается, когда сам класс определяется, а не когда создается первый экземпляр. Я бы предположил, что этот метод динамического создания тестовых методов более совместим с интроспекцией, используемой unittest
, для определения количества тестов в классе (т. Е. Он может скомпилировать список тестов, прежде чем он когда-либо создаст экземпляр этого класса).
– BillyBBone
23 February 2016 в 05:20
class TestSequence(unittest.TestCase, metaclass=TestSequenceMeta):[...]
– Mathieu_Du
30 December 2016 в 19:40
Супер опоздал на вечеринку, но мне не удалось сделать эти работы для setUpClass
.
Вот версия ответа @ Хавьера , которая дает setUpClass
доступ к динамическому выделенные атрибуты.
import unittest
class GeneralTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
print ''
print cls.p1
print cls.p2
def runTest1(self):
self.assertTrue((self.p2 - self.p1) == 1)
def runTest2(self):
self.assertFalse((self.p2 - self.p1) == 2)
def load_tests(loader, tests, pattern):
test_cases = unittest.TestSuite()
for p1, p2 in [(1, 2), (3, 4)]:
clsname = 'TestCase_{}_{}'.format(p1, p2)
dct = {
'p1': p1,
'p2': p2,
}
cls = type(clsname, (GeneralTestCase,), dct)
test_cases.addTest(cls('runTest1'))
test_cases.addTest(cls('runTest2'))
return test_cases
Выходы
1
2
..
3
4
..
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
Также существует гипотеза, которая добавляет тестирование на основе fuzz или свойства: https://pypi.python.org/pypi/hypothesis
Это очень мощный метод тестирования.
Я использую метаклассы и декораторы для генерации тестов. Вы можете проверить мою реализацию python_wrap_cases .
Ваш пример:
import unittest
from python_wrap_cases import wrap_case
@wrap_case
class TestSequence(unittest.TestCase):
@wrap_case("foo", "a", "a")
@wrap_case("bar", "a", "b")
@wrap_case("lee", "b", "b")
def testsample(self, name, a, b):
print "test", name
self.assertEqual(a, b)
Консольный выход:
testsample_u'bar'_u'a'_u'b' (tests.example.test_stackoverflow.TestSequence) ... test bar
FAIL
testsample_u'foo'_u'a'_u'a' (tests.example.test_stackoverflow.TestSequence) ... test foo
ok
testsample_u'lee'_u'b'_u'b' (tests.example.test_stackoverflow.TestSequence) ... test lee
ok
Также вы можете использовать генераторы . Например, этот код генерирует все возможные комбинации тестов с аргументами a__list
и b__list
import unittest
from python_wrap_cases import wrap_case
@wrap_case
class TestSequence(unittest.TestCase):
@wrap_case(a__list=["a", "b"], b__list=["a", "b"])
def testsample(self, a, b):
self.assertEqual(a, b)
Выход консоли:
testsample_a(u'a')_b(u'a') (tests.example.test_stackoverflow.TestSequence) ... ok
testsample_a(u'a')_b(u'b') (tests.example.test_stackoverflow.TestSequence) ... FAIL
testsample_a(u'b')_b(u'a') (tests.example.test_stackoverflow.TestSequence) ... FAIL
testsample_a(u'b')_b(u'b') (tests.example.test_stackoverflow.TestSequence) ... ok
Помимо использования setattr, мы можем использовать load_tests с python 3.2. Пожалуйста, обратитесь к сообщению в блоге blog.livreuro.com/en/coding/python/how-to-generate-discoverable-unit-tests-in-python-dynamically /
class Test(unittest.TestCase):
pass
def _test(self, file_name):
open(file_name, 'r') as f:
self.assertEqual('test result',f.read())
def _generate_test(file_name):
def test(self):
_test(self, file_name)
return test
def _generate_tests():
for file in files:
file_name = os.path.splitext(os.path.basename(file))[0]
setattr(Test, 'test_%s' % file_name, _generate_test(file))
test_cases = (Test,)
def load_tests(loader, tests, pattern):
_generate_tests()
suite = TestSuite()
for test_class in test_cases:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
return suite
if __name__ == '__main__':
_generate_tests()
unittest.main()
Вы можете использовать плагин нос-ittr (pip install nose-ittr
).
Очень легко интегрироваться с существующими тестами, минимальные изменения (если они есть) требуются. Он также поддерживает плагин многопроцессорной обработки носа .
Нельзя также настроить функцию setup
для каждого теста.
@ittr(number=[1, 2, 3, 4])
def test_even(self):
assert_equal(self.number % 2, 0)
Также возможно для передачи параметров nosetest
, например, с их встроенным плагином attrib
, таким образом вы можете запустить только конкретный тест с определенным параметром:
nosetest -a number=2
На днях я столкнулся с ParamUnittest , когда смотрел исходный код на использование примера радона ( в github repo ). Он должен работать с другими структурами, расширяющими TestCase (например, Nose).
Вот пример:
import unittest
import paramunittest
@paramunittest.parametrized(
('1', '2'),
#(4, 3), <---- uncomment to have a failing test
('2', '3'),
(('4', ), {'b': '5'}),
((), {'a': 5, 'b': 6}),
{'a': 5, 'b': 6},
)
class TestBar(TestCase):
def setParameters(self, a, b):
self.a = a
self.b = b
def testLess(self):
self.assertLess(self.a, self.b)
Это решение работает с unittest
и nose
:
#!/usr/bin/env python
import unittest
def make_function(description, a, b):
def ghost(self):
self.assertEqual(a, b, description)
print description
ghost.__name__ = 'test_{0}'.format(description)
return ghost
class TestsContainer(unittest.TestCase):
pass
testsmap = {
'foo': [1, 1],
'bar': [1, 2],
'baz': [5, 5]}
def generator():
for name, params in testsmap.iteritems():
test_func = make_function(name, params[0], params[1])
setattr(TestsContainer, 'test_{0}'.format(name), test_func)
generator()
if __name__ == '__main__':
unittest.main()
Ответы на основе метакласса по-прежнему работают в Python3, но вместо атрибута __metaclass__
нужно использовать параметр metaclass
, как в:
class ExampleTestCase(TestCase,metaclass=DocTestMeta):
pass
Следующее - мое решение. Я считаю это полезным, когда: 1. Должен работать для unittest.Testcase и unittest обнаружить 2. Попробуйте выполнить набор тестов для разных параметров. 3. Очень простая зависимость от других пакетов import unittest
class BaseClass(unittest.TestCase):
def setUp(self):
self.param = 2
self.base = 2
def test_me(self):
self.assertGreaterEqual(5, self.param+self.base)
def test_me_too(self):
self.assertLessEqual(3, self.param+self.base)
class Child_One(BaseClass):
def setUp(self):
BaseClass.setUp(self)
self.param = 4
class Child_Two(BaseClass):
def setUp(self):
BaseClass.setUp(self)
self.param = 1
Это можно сделать, используя pytest . Просто напишите файл test_me.py
с контентом:
import pytest
@pytest.mark.parametrize('name, left, right', [['foo', 'a', 'a'],
['bar', 'a', 'b'],
['baz', 'b', 'b']])
def test_me(name, left, right):
assert left == right, name
И запустите свой тест командой py.test --tb=short test_me.py
. Тогда выход будет выглядеть так:
=========================== test session starts ============================
platform darwin -- Python 2.7.6 -- py-1.4.23 -- pytest-2.6.1
collected 3 items
test_me.py .F.
================================= FAILURES =================================
_____________________________ test_me[bar-a-b] _____________________________
test_me.py:8: in test_me
assert left == right, name
E AssertionError: bar
==================== 1 failed, 2 passed in 0.01 seconds ====================
Это просто !. Кроме того, pytest имеет больше функций, таких как fixtures
, mark
, assert
и т. Д. ...
py.test
– Sergey Voronezhskiy
25 March 2016 в 16:55
unittest
на py.test. Раньше у меня были базовые классы TestCase
, которые могли динамически создавать детей с разными аргументами, которые они сохраняли бы как переменные класса ... что было немного громоздким.
– timgeb
25 March 2016 в 16:57
py.test
является yield_fixtures . Что можно сделать setup i>, вернуть некоторые полезные данные в тест и после завершения теста сделать teardown i>. Светильники также могут быть параметризированы .
– Sergey Voronezhskiy
25 March 2016 в 17:08
Просто используйте метаклассы, как показано здесь:
class DocTestMeta(type):
"""
Test functions are generated in metaclass due to the way some
test loaders work. For example, setupClass() won't get called
unless there are other existing test methods, and will also
prevent unit test loader logic being called before the test
methods have been defined.
"""
def __init__(self, name, bases, attrs):
super(DocTestMeta, self).__init__(name, bases, attrs)
def __new__(cls, name, bases, attrs):
def func(self):
"""Inner test method goes here"""
self.assertTrue(1)
func.__name__ = 'test_sample'
attrs[func.__name__] = func
return super(DocTestMeta, cls).__new__(cls, name, bases, attrs)
class ExampleTestCase(TestCase):
"""Our example test case, with no methods defined"""
__metaclass__ = DocTestMeta
Выход:
test_sample (ExampleTestCase) ... OK
Мета-программирование - это весело, но может быть в пути. Большинство решений здесь затрудняют:
Итак, мое первое предложение состоит в том, чтобы следовать простому / явному пути (работает с любым тестовым бегуном):
import unittest
class TestSequence(unittest.TestCase):
def _test_complex_property(self, a, b):
self.assertEqual(a,b)
def test_foo(self):
self._test_complex_property("a", "a")
def test_bar(self):
self._test_complex_property("a", "b")
def test_lee(self):
self._test_complex_property("b", "b")
if __name__ == '__main__':
unittest.main()
Поскольку мы не должны повторять себя, мое второе предложение основывается на ответе @ Хавьера: включите тестирование на основе свойств , Библиотека гипотез:
@given(st.text(), st.text())
def test_complex_property(self, a, b):
self.assertEqual(a,b)
Чтобы проверить свои конкретные примеры, просто добавьте:
@example("a", "a")
@example("a", "b")
@example("b", "b")
Чтобы запустить только один конкретный пример, вы можете прокомментировать другие примеры (при условии, что пример будет запущен первым). Вы можете использовать @given(st.nothing())
. Другой вариант - заменить весь блок на:
@given(st.just("a"), st.just("b"))
Хорошо, у вас нет разных имен тестов. Но, возможно, вам просто нужно:
.__name__ =
для включения.exact_method
i> тестирование – Nakilon 12 April 2013 в 11:38if __name__ == '__main__'
условном? Разумеется, он должен выйти за пределы этого, чтобы работать во время импорта (помня, что модули python импортируются только один раз, даже если они импортированы из нескольких разных мест) – SpoonMeiser 21 December 2013 в 02:52