Running unittest Tests
The pytest collects the unittest.TestCase classes (the Test* the standard library's), so the legacy unittest suite runs under the pytest command without the python -m unittest - the setUp/tearDown map to the setup/teardown, the self.assertEqual and friends work (the no rewriting, the unittest's own message), and the addCleanup the finalizer. The unittest-specific asserts (assertRaises, the assertAlmostEqual) are available on the self, and the pytest fixtures can be injected into the TestCase method (the parameter name, the unittest 3.12+ the subTest the loop). This chapter covers the TestCase collection (the setUp/tearDown the mapping), the self.assert* (the no rewrite, the message), and the fixture injection into the TestCase (the parameter, the subTest).
The TestCase Collection
# the unittest.TestCase: the the pytest COLLECTS it
# (the the Test* the method,
# the setUp/tearDown the setup)
import unittest
from texttools import word_count
class TestWordCount(unittest.TestCase):
def setUp(self):
self.text = "the quick brown fox"
def test_count(self):
self.assertEqual(word_count(self.text), 4)
def test_empty(self):
self.assertEqual(word_count(""), 0)
def tearDown(self):
self.text = None # the cleanup
# the the COLLECTION: the the TestWordCount::test_count,
# TestWordCount::test_empty (the
# the)
# the the SETUP: the the setUp BEFORE each test (the
# the fresh, the)
The pytest collects the unittest.TestCase by the Test* name and the test_* method (the TestWordCount::test_count the item), and the setUp/tearDown run around each test (the fresh instance, the setUp before, the tearDown after - the same lifecycle the unittest gives). The self.assert* methods work as the unittest wrote them (the assertEqual, the assertIn, the assertRaises context) - the no assertion rewriting (the unittest's own message, the no where line), and the addCleanup is the finalizer (the self.addCleanup(self.close) the after). The migration is the gradual: the legacy unittest suite runs under the pytest (the one command, the one report), and the new tests use the bare assert plus the fixture (the rewrite, the no boilerplate) - the two coexist, and the next section's fixture injection is the bridge the TestCase reaches the pytest fixture.
The self.assert* and the subTest
# the self.assert*: the the unittest's OWN message
# (the no
# the rewrite, the)
import unittest
from texttools import word_count
class TestAsserts(unittest.TestCase):
def test_assertEquals(self):
self.assertEqual(word_count("a b"), 2,
"the count the the")
# the msg the optional
# (the)
def test_assertRaises(self):
with self.assertRaises(ValueError):
... # the
def test_subTest(self):
# the subTest: the LOOP the
# per-case (the
# the)
for text, expected in [("", 0), ("a", 1)]:
with self.subTest(text=text):
self.assertEqual(word_count(text),
expected)
# the the subTest: the
# per-case IDENTIFIER (the failure
# the [text=''] the case)
The self.assert* is the unittest's own assertion (the assertEqual the msg the optional, the assertRaises the context, the assertAlmostEqual the float) - the message is the unittest's (the 2 != 1, the no where line the rewrite does not reach the method call), and the subTest is the loop's per-case (the with self.subTest(text=text) the identifier, the failure the [text=''] the case, the rest continues - the subTest is the unittest's answer to the loop-in-test, the parametrize the pytest's). The fixture injection is the bridge: the TestCase method can request the pytest fixture by the parameter name (the def test_x(self, fixture_name), the unittest 3.12+ the subTest the loop) - the conftest fixture reaches the TestCase, and the migration is the fixture-first (the new setup the fixture, the legacy the setUp). The rule is the one: the legacy unittest runs under the pytest (the one command), the new tests use the bare assert plus the fixture (the rewrite), and the subTest is the loop's per-case the unittest keeps - the next chapter's mock is the dependency stub the both styles use.
The Working Rules
- The
TestCaseis collected, thesetUp/tearDownthe mapping. TheTestWordCount::test_countthe item, the fresh instance per test, theaddCleanupthe finalizer - the legacy suite runs under thepytestcommand, the one report. - The
self.assert*is theunittest's message, the no rewrite. TheassertEqualthemsg, theassertRaisesthe context, theassertAlmostEqualthe float - the method call the rewriter does not reach, the message theunittest's own. - The
subTestis the loop's per-case. Thewith self.subTest(text=text)the identifier, the failure the[text='']the case, the rest continues - theunittest's answer to the loop-in-test, theparametrizethe pytest's equivalent. - The fixture injection is the bridge. The
TestCasemethod requests the pytest fixture by the parameter name (the 3.12+), theconftestreaches theTestCase- the migration is the fixture-first (the new setup the fixture, the legacy thesetUp).
Summary
The pytest collects the unittest.TestCase by the Test* name and the test_* method (the TestWordCount::test_count the item), and the setUp/tearDown run around each test (the fresh instance, the addCleanup the finalizer) - the legacy suite runs under the pytest command without the python -m unittest. The self.assert* methods work as the unittest wrote them (the assertEqual the msg, the assertRaises the context) with the unittest's own message (the no assertion rewrite on the method call), and the subTest is the loop's per-case (the [text=''] the identifier, the rest continues). The fixture injection (the TestCase method requests the pytest fixture by the parameter name, the 3.12+) is the bridge, and the migration is the gradual: the legacy unittest under the pytest, the new tests the bare assert plus the fixture. The next chapter's unittest.mock is the dependency stub both styles use - the patch, the return_value, and the assert_called the mock's assertion.