Skip to content

Commit 45d2d49

Browse files
serhiy-storchakacjw296
authored andcommitted
gh-71339: Add additional assertion methods for unittest (GH-128707)
Add the following methods: * assertHasAttr() and assertNotHasAttr() * assertIsSubclass() and assertNotIsSubclass() * assertStartsWith() and assertNotStartsWith() * assertEndsWith() and assertNotEndsWith() Also improve error messages for assertIsInstance() and assertNotIsInstance(). Backports: 06cad77a5b345adde88609be9c3c470c5cd9f417 Signed-off-by: Chris Withers <chris@withers.org>
1 parent 9e3f49c commit 45d2d49

7 files changed

+41
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Add new assertion methods for :mod:`unittest`:
2+
:meth:`~unittest.TestCase.assertHasAttr`,
3+
:meth:`~unittest.TestCase.assertNotHasAttr`,
4+
:meth:`~unittest.TestCase.assertIsSubclass`,
5+
:meth:`~unittest.TestCase.assertNotIsSubclass`
6+
:meth:`~unittest.TestCase.assertStartsWith`,
7+
:meth:`~unittest.TestCase.assertNotStartsWith`,
8+
:meth:`~unittest.TestCase.assertEndsWith` and
9+
:meth:`~unittest.TestCase.assertNotEndsWith`.

mock/tests/testasync.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -600,16 +600,16 @@ def test_sync_magic_methods_return_magic_mocks(self):
600600

601601
def test_magicmock_has_async_magic_methods(self):
602602
m_mock = MagicMock()
603-
self.assertTrue(hasattr(m_mock, "__aenter__"))
604-
self.assertTrue(hasattr(m_mock, "__aexit__"))
605-
self.assertTrue(hasattr(m_mock, "__anext__"))
603+
self.assertHasAttr(m_mock, "__aenter__")
604+
self.assertHasAttr(m_mock, "__aexit__")
605+
self.assertHasAttr(m_mock, "__anext__")
606606

607607
def test_asyncmock_has_sync_magic_methods(self):
608608
a_mock = AsyncMock()
609-
self.assertTrue(hasattr(a_mock, "__enter__"))
610-
self.assertTrue(hasattr(a_mock, "__exit__"))
611-
self.assertTrue(hasattr(a_mock, "__next__"))
612-
self.assertTrue(hasattr(a_mock, "__len__"))
609+
self.assertHasAttr(a_mock, "__enter__")
610+
self.assertHasAttr(a_mock, "__exit__")
611+
self.assertHasAttr(a_mock, "__next__")
612+
self.assertHasAttr(a_mock, "__len__")
613613

614614
def test_magic_methods_are_async_functions(self):
615615
m_mock = MagicMock()

mock/tests/testcallable.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,35 @@ def assertNotCallable(self, mock):
2222
def test_non_callable(self):
2323
for mock in NonCallableMagicMock(), NonCallableMock():
2424
self.assertRaises(TypeError, mock)
25-
self.assertFalse(hasattr(mock, '__call__'))
25+
self.assertNotHasAttr(mock, '__call__')
2626
self.assertIn(mock.__class__.__name__, repr(mock))
2727

2828

2929
def test_hierarchy(self):
30-
self.assertTrue(issubclass(MagicMock, Mock))
31-
self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock))
30+
self.assertIsSubclass(MagicMock, Mock)
31+
self.assertIsSubclass(NonCallableMagicMock, NonCallableMock)
3232

3333

3434
def test_attributes(self):
3535
one = NonCallableMock()
36-
self.assertTrue(issubclass(type(one.one), Mock))
36+
self.assertIsSubclass(type(one.one), Mock)
3737

3838
two = NonCallableMagicMock()
39-
self.assertTrue(issubclass(type(two.two), MagicMock))
39+
self.assertIsSubclass(type(two.two), MagicMock)
4040

4141

4242
def test_subclasses(self):
4343
class MockSub(Mock):
4444
pass
4545

4646
one = MockSub()
47-
self.assertTrue(issubclass(type(one.one), MockSub))
47+
self.assertIsSubclass(type(one.one), MockSub)
4848

4949
class MagicSub(MagicMock):
5050
pass
5151

5252
two = MagicSub()
53-
self.assertTrue(issubclass(type(two.two), MagicSub))
53+
self.assertIsSubclass(type(two.two), MagicSub)
5454

5555

5656
def test_patch_spec(self):

mock/tests/testhelpers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ def __getattr__(self, attribute):
965965

966966
proxy = Foo()
967967
autospec = create_autospec(proxy)
968-
self.assertFalse(hasattr(autospec, '__name__'))
968+
self.assertNotHasAttr(autospec, '__name__')
969969

970970

971971
def test_autospec_signature_staticmethod(self):

mock/tests/testmagicmethods.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ class TestMockingMagicMethods(unittest.TestCase):
1010

1111
def test_deleting_magic_methods(self):
1212
mock = Mock()
13-
self.assertFalse(hasattr(mock, '__getitem__'))
13+
self.assertNotHasAttr(mock, '__getitem__')
1414

1515
mock.__getitem__ = Mock()
16-
self.assertTrue(hasattr(mock, '__getitem__'))
16+
self.assertHasAttr(mock, '__getitem__')
1717

1818
del mock.__getitem__
19-
self.assertFalse(hasattr(mock, '__getitem__'))
19+
self.assertNotHasAttr(mock, '__getitem__')
2020

2121

2222
def test_magicmock_del(self):
@@ -252,12 +252,12 @@ def test_magicmock(self):
252252
self.assertEqual(list(mock), [1, 2, 3])
253253

254254
getattr(mock, '__bool__').return_value = False
255-
self.assertFalse(hasattr(mock, '__nonzero__'))
255+
self.assertNotHasAttr(mock, '__nonzero__')
256256
self.assertFalse(bool(mock))
257257

258258
for entry in _magics:
259-
self.assertTrue(hasattr(mock, entry))
260-
self.assertFalse(hasattr(mock, '__imaginary__'))
259+
self.assertHasAttr(mock, entry)
260+
self.assertNotHasAttr(mock, '__imaginary__')
261261

262262

263263
def test_magic_mock_equality(self):

mock/tests/testmock.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -2225,13 +2225,13 @@ def test_attach_mock_patch_autospec_signature(self):
22252225
def test_attribute_deletion(self):
22262226
for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
22272227
NonCallableMock()):
2228-
self.assertTrue(hasattr(mock, 'm'))
2228+
self.assertHasAttr(mock, 'm')
22292229

22302230
del mock.m
2231-
self.assertFalse(hasattr(mock, 'm'))
2231+
self.assertNotHasAttr(mock, 'm')
22322232

22332233
del mock.f
2234-
self.assertFalse(hasattr(mock, 'f'))
2234+
self.assertNotHasAttr(mock, 'f')
22352235
self.assertRaises(AttributeError, getattr, mock, 'f')
22362236

22372237

@@ -2240,18 +2240,18 @@ def test_mock_does_not_raise_on_repeated_attribute_deletion(self):
22402240
for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
22412241
NonCallableMock()):
22422242
mock.foo = 3
2243-
self.assertTrue(hasattr(mock, 'foo'))
2243+
self.assertHasAttr(mock, 'foo')
22442244
self.assertEqual(mock.foo, 3)
22452245

22462246
del mock.foo
2247-
self.assertFalse(hasattr(mock, 'foo'))
2247+
self.assertNotHasAttr(mock, 'foo')
22482248

22492249
mock.foo = 4
2250-
self.assertTrue(hasattr(mock, 'foo'))
2250+
self.assertHasAttr(mock, 'foo')
22512251
self.assertEqual(mock.foo, 4)
22522252

22532253
del mock.foo
2254-
self.assertFalse(hasattr(mock, 'foo'))
2254+
self.assertNotHasAttr(mock, 'foo')
22552255

22562256

22572257
def test_mock_raises_when_deleting_nonexistent_attribute(self):
@@ -2269,7 +2269,7 @@ def test_reset_mock_does_not_raise_on_attr_deletion(self):
22692269
mock.child = True
22702270
del mock.child
22712271
mock.reset_mock()
2272-
self.assertFalse(hasattr(mock, 'child'))
2272+
self.assertNotHasAttr(mock, 'child')
22732273

22742274

22752275
def test_class_assignable(self):

mock/tests/testpatch.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def test():
364364
self.assertEqual(SomeClass.frooble, sentinel.Frooble)
365365

366366
test()
367-
self.assertFalse(hasattr(SomeClass, 'frooble'))
367+
self.assertNotHasAttr(SomeClass, 'frooble')
368368

369369

370370
def test_patch_wont_create_by_default(self):
@@ -381,7 +381,7 @@ def test_patchobject_wont_create_by_default(self):
381381
@patch.object(SomeClass, 'ord', sentinel.Frooble)
382382
def test(): pass
383383
test()
384-
self.assertFalse(hasattr(SomeClass, 'ord'))
384+
self.assertNotHasAttr(SomeClass, 'ord')
385385

386386

387387
def test_patch_builtins_without_create(self):
@@ -1475,7 +1475,7 @@ def test_patch_multiple_create(self):
14751475
finally:
14761476
patcher.stop()
14771477

1478-
self.assertFalse(hasattr(Foo, 'blam'))
1478+
self.assertNotHasAttr(Foo, 'blam')
14791479

14801480

14811481
def test_patch_multiple_spec_set(self):

0 commit comments

Comments
 (0)