gh-71339: Use new assertion methods in tests (GH-129046)

diff --git a/Lib/test/_test_embed_structseq.py b/Lib/test/_test_embed_structseq.py
index 154662e..4cac84d 100644
--- a/Lib/test/_test_embed_structseq.py
+++ b/Lib/test/_test_embed_structseq.py
@@ -11,7 +11,7 @@ def check_structseq(self, obj_type):
         # ob_refcnt
         self.assertGreaterEqual(sys.getrefcount(obj_type), 1)
         # tp_base
-        self.assertTrue(issubclass(obj_type, tuple))
+        self.assertIsSubclass(obj_type, tuple)
         # tp_bases
         self.assertEqual(obj_type.__bases__, (tuple,))
         # tp_dict
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index df5e45f..93b3382 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -183,7 +183,7 @@ class NotEnough(tzinfo):
             def __init__(self, offset, name):
                 self.__offset = offset
                 self.__name = name
-        self.assertTrue(issubclass(NotEnough, tzinfo))
+        self.assertIsSubclass(NotEnough, tzinfo)
         ne = NotEnough(3, "NotByALongShot")
         self.assertIsInstance(ne, tzinfo)
 
@@ -232,7 +232,7 @@ def test_pickling_subclass(self):
                 self.assertIs(type(derived), otype)
                 self.assertEqual(derived.utcoffset(None), offset)
                 self.assertEqual(derived.tzname(None), oname)
-                self.assertFalse(hasattr(derived, 'spam'))
+                self.assertNotHasAttr(derived, 'spam')
 
     def test_issue23600(self):
         DSTDIFF = DSTOFFSET = timedelta(hours=1)
@@ -813,7 +813,7 @@ def test_roundtrip(self):
 
             # Verify td -> string -> td identity.
             s = repr(td)
-            self.assertTrue(s.startswith('datetime.'))
+            self.assertStartsWith(s, 'datetime.')
             s = s[9:]
             td2 = eval(s)
             self.assertEqual(td, td2)
@@ -1231,7 +1231,7 @@ def test_roundtrip(self):
                    self.theclass.today()):
             # Verify dt -> string -> date identity.
             s = repr(dt)
-            self.assertTrue(s.startswith('datetime.'))
+            self.assertStartsWith(s, 'datetime.')
             s = s[9:]
             dt2 = eval(s)
             self.assertEqual(dt, dt2)
@@ -2218,7 +2218,7 @@ def test_roundtrip(self):
                    self.theclass.now()):
             # Verify dt -> string -> datetime identity.
             s = repr(dt)
-            self.assertTrue(s.startswith('datetime.'))
+            self.assertStartsWith(s, 'datetime.')
             s = s[9:]
             dt2 = eval(s)
             self.assertEqual(dt, dt2)
@@ -3687,7 +3687,7 @@ def test_roundtrip(self):
 
         # Verify t -> string -> time identity.
         s = repr(t)
-        self.assertTrue(s.startswith('datetime.'))
+        self.assertStartsWith(s, 'datetime.')
         s = s[9:]
         t2 = eval(s)
         self.assertEqual(t, t2)
diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py
index 9d38da5..20306e1 100644
--- a/Lib/test/mapping_tests.py
+++ b/Lib/test/mapping_tests.py
@@ -70,8 +70,8 @@ def test_read(self):
         if not d: self.fail("Full mapping must compare to True")
         # keys(), items(), iterkeys() ...
         def check_iterandlist(iter, lst, ref):
-            self.assertTrue(hasattr(iter, '__next__'))
-            self.assertTrue(hasattr(iter, '__iter__'))
+            self.assertHasAttr(iter, '__next__')
+            self.assertHasAttr(iter, '__iter__')
             x = list(iter)
             self.assertTrue(set(x)==set(lst)==set(ref))
         check_iterandlist(iter(d.keys()), list(d.keys()),
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index dcba636..9d6ae3e 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -3068,7 +3068,7 @@ def test_proto(self):
             pickled = self.dumps(None, proto)
             if proto >= 2:
                 proto_header = pickle.PROTO + bytes([proto])
-                self.assertTrue(pickled.startswith(proto_header))
+                self.assertStartsWith(pickled, proto_header)
             else:
                 self.assertEqual(count_opcode(pickle.PROTO, pickled), 0)
 
@@ -5007,7 +5007,7 @@ def test_default_dispatch_table(self):
         p = self.pickler_class(f, 0)
         with self.assertRaises(AttributeError):
             p.dispatch_table
-        self.assertFalse(hasattr(p, 'dispatch_table'))
+        self.assertNotHasAttr(p, 'dispatch_table')
 
     def test_class_dispatch_table(self):
         # A dispatch_table attribute can be specified class-wide
diff --git a/Lib/test/support/warnings_helper.py b/Lib/test/support/warnings_helper.py
index a6e43df..5f6f14a 100644
--- a/Lib/test/support/warnings_helper.py
+++ b/Lib/test/support/warnings_helper.py
@@ -23,8 +23,7 @@ def check_syntax_warning(testcase, statement, errtext='',
     testcase.assertEqual(len(warns), 1, warns)
 
     warn, = warns
-    testcase.assertTrue(issubclass(warn.category, SyntaxWarning),
-                        warn.category)
+    testcase.assertIsSubclass(warn.category, SyntaxWarning)
     if errtext:
         testcase.assertRegex(str(warn.message), errtext)
     testcase.assertEqual(warn.filename, '<testcase>')
diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py
index 53aa266..0813c48 100644
--- a/Lib/test/test__osx_support.py
+++ b/Lib/test/test__osx_support.py
@@ -66,8 +66,8 @@ def test__find_build_tool(self):
                             'cc not found - check xcode-select')
 
     def test__get_system_version(self):
-        self.assertTrue(platform.mac_ver()[0].startswith(
-                                    _osx_support._get_system_version()))
+        self.assertStartsWith(platform.mac_ver()[0],
+                              _osx_support._get_system_version())
 
     def test__remove_original_values(self):
         config_vars = {
diff --git a/Lib/test/test_abstract_numbers.py b/Lib/test/test_abstract_numbers.py
index 72232b6..cf071d2 100644
--- a/Lib/test/test_abstract_numbers.py
+++ b/Lib/test/test_abstract_numbers.py
@@ -24,11 +24,11 @@ def not_implemented(*args, **kwargs):
 
 class TestNumbers(unittest.TestCase):
     def test_int(self):
-        self.assertTrue(issubclass(int, Integral))
-        self.assertTrue(issubclass(int, Rational))
-        self.assertTrue(issubclass(int, Real))
-        self.assertTrue(issubclass(int, Complex))
-        self.assertTrue(issubclass(int, Number))
+        self.assertIsSubclass(int, Integral)
+        self.assertIsSubclass(int, Rational)
+        self.assertIsSubclass(int, Real)
+        self.assertIsSubclass(int, Complex)
+        self.assertIsSubclass(int, Number)
 
         self.assertEqual(7, int(7).real)
         self.assertEqual(0, int(7).imag)
@@ -38,11 +38,11 @@ def test_int(self):
         self.assertEqual(1, int(7).denominator)
 
     def test_float(self):
-        self.assertFalse(issubclass(float, Integral))
-        self.assertFalse(issubclass(float, Rational))
-        self.assertTrue(issubclass(float, Real))
-        self.assertTrue(issubclass(float, Complex))
-        self.assertTrue(issubclass(float, Number))
+        self.assertNotIsSubclass(float, Integral)
+        self.assertNotIsSubclass(float, Rational)
+        self.assertIsSubclass(float, Real)
+        self.assertIsSubclass(float, Complex)
+        self.assertIsSubclass(float, Number)
 
         self.assertEqual(7.3, float(7.3).real)
         self.assertEqual(0, float(7.3).imag)
@@ -50,11 +50,11 @@ def test_float(self):
         self.assertEqual(-7.3, float(-7.3).conjugate())
 
     def test_complex(self):
-        self.assertFalse(issubclass(complex, Integral))
-        self.assertFalse(issubclass(complex, Rational))
-        self.assertFalse(issubclass(complex, Real))
-        self.assertTrue(issubclass(complex, Complex))
-        self.assertTrue(issubclass(complex, Number))
+        self.assertNotIsSubclass(complex, Integral)
+        self.assertNotIsSubclass(complex, Rational)
+        self.assertNotIsSubclass(complex, Real)
+        self.assertIsSubclass(complex, Complex)
+        self.assertIsSubclass(complex, Number)
 
         c1, c2 = complex(3, 2), complex(4,1)
         # XXX: This is not ideal, but see the comment in math_trunc().
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 58853ba..08ff413 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -6805,7 +6805,7 @@ class TestImportStar(TestCase):
 
     def test(self):
         for name in argparse.__all__:
-            self.assertTrue(hasattr(argparse, name))
+            self.assertHasAttr(argparse, name)
 
     def test_all_exports_everything_but_modules(self):
         items = [
diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index 0776559..1479a8e 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -275,12 +275,12 @@ def test_alias(self):
         self.assertEqual(alias.end_col_offset, 17)
 
     def test_base_classes(self):
-        self.assertTrue(issubclass(ast.For, ast.stmt))
-        self.assertTrue(issubclass(ast.Name, ast.expr))
-        self.assertTrue(issubclass(ast.stmt, ast.AST))
-        self.assertTrue(issubclass(ast.expr, ast.AST))
-        self.assertTrue(issubclass(ast.comprehension, ast.AST))
-        self.assertTrue(issubclass(ast.Gt, ast.AST))
+        self.assertIsSubclass(ast.For, ast.stmt)
+        self.assertIsSubclass(ast.Name, ast.expr)
+        self.assertIsSubclass(ast.stmt, ast.AST)
+        self.assertIsSubclass(ast.expr, ast.AST)
+        self.assertIsSubclass(ast.comprehension, ast.AST)
+        self.assertIsSubclass(ast.Gt, ast.AST)
 
     def test_field_attr_existence(self):
         for name, item in ast.__dict__.items():
@@ -1101,7 +1101,7 @@ def test_copy_with_parents(self):
     def test_replace_interface(self):
         for klass in self.iter_ast_classes():
             with self.subTest(klass=klass):
-                self.assertTrue(hasattr(klass, '__replace__'))
+                self.assertHasAttr(klass, '__replace__')
 
             fields = set(klass._fields)
             with self.subTest(klass=klass, fields=fields):
@@ -1330,7 +1330,7 @@ def test_replace_reject_known_custom_instance_fields_commits(self):
         context = node.ctx
 
         # explicit rejection of known instance fields
-        self.assertTrue(hasattr(node, 'extra'))
+        self.assertHasAttr(node, 'extra')
         msg = "Name.__replace__ got an unexpected keyword argument 'extra'."
         with self.assertRaisesRegex(TypeError, re.escape(msg)):
             copy.replace(node, extra=1)
@@ -3071,7 +3071,7 @@ def test_FunctionDef(self):
         with self.assertWarnsRegex(DeprecationWarning,
                                    r"FunctionDef\.__init__ missing 1 required positional argument: 'name'"):
             node = ast.FunctionDef(args=args)
-        self.assertFalse(hasattr(node, "name"))
+        self.assertNotHasAttr(node, "name")
         self.assertEqual(node.decorator_list, [])
         node = ast.FunctionDef(name='foo', args=args)
         self.assertEqual(node.name, 'foo')
diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py
index 2b24b5d..5f9eb38 100644
--- a/Lib/test/test_audit.py
+++ b/Lib/test/test_audit.py
@@ -134,7 +134,7 @@ def test_socket(self):
         self.assertEqual(events[0][0], "socket.gethostname")
         self.assertEqual(events[1][0], "socket.__new__")
         self.assertEqual(events[2][0], "socket.bind")
-        self.assertTrue(events[2][2].endswith("('127.0.0.1', 8080)"))
+        self.assertEndsWith(events[2][2], "('127.0.0.1', 8080)")
 
     def test_gc(self):
         returncode, events, stderr = self.run_python("test_gc")
diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py
index 9efebc4..ce2e3e3 100644
--- a/Lib/test/test_base64.py
+++ b/Lib/test/test_base64.py
@@ -812,7 +812,7 @@ def test_decode_nonascii_str(self):
             self.assertRaises(ValueError, f, 'with non-ascii \xcb')
 
     def test_ErrorHeritage(self):
-        self.assertTrue(issubclass(binascii.Error, ValueError))
+        self.assertIsSubclass(binascii.Error, ValueError)
 
     def test_RFC4648_test_cases(self):
         # test cases from RFC 4648 section 10
diff --git a/Lib/test/test_baseexception.py b/Lib/test/test_baseexception.py
index e599b02..12d4088 100644
--- a/Lib/test/test_baseexception.py
+++ b/Lib/test/test_baseexception.py
@@ -10,13 +10,11 @@ class ExceptionClassTests(unittest.TestCase):
     inheritance hierarchy)"""
 
     def test_builtins_new_style(self):
-        self.assertTrue(issubclass(Exception, object))
+        self.assertIsSubclass(Exception, object)
 
     def verify_instance_interface(self, ins):
         for attr in ("args", "__str__", "__repr__"):
-            self.assertTrue(hasattr(ins, attr),
-                    "%s missing %s attribute" %
-                        (ins.__class__.__name__, attr))
+            self.assertHasAttr(ins, attr)
 
     def test_inheritance(self):
         # Make sure the inheritance hierarchy matches the documentation
@@ -65,7 +63,7 @@ def test_inheritance(self):
                 elif last_depth > depth:
                     while superclasses[-1][0] >= depth:
                         superclasses.pop()
-                self.assertTrue(issubclass(exc, superclasses[-1][1]),
+                self.assertIsSubclass(exc, superclasses[-1][1],
                 "%s is not a subclass of %s" % (exc.__name__,
                     superclasses[-1][1].__name__))
                 try:  # Some exceptions require arguments; just skip them
diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py
index 1f3b674..7ed7d7c 100644
--- a/Lib/test/test_binascii.py
+++ b/Lib/test/test_binascii.py
@@ -38,13 +38,13 @@ def assertConversion(self, original, converted, restored, **kwargs):
 
     def test_exceptions(self):
         # Check module exceptions
-        self.assertTrue(issubclass(binascii.Error, Exception))
-        self.assertTrue(issubclass(binascii.Incomplete, Exception))
+        self.assertIsSubclass(binascii.Error, Exception)
+        self.assertIsSubclass(binascii.Incomplete, Exception)
 
     def test_functions(self):
         # Check presence of all functions
         for name in all_functions:
-            self.assertTrue(hasattr(getattr(binascii, name), '__call__'))
+            self.assertHasAttr(getattr(binascii, name), '__call__')
             self.assertRaises(TypeError, getattr(binascii, name))
 
     def test_returned_value(self):
diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py
index 299af09..b224c3d 100644
--- a/Lib/test/test_binop.py
+++ b/Lib/test/test_binop.py
@@ -383,7 +383,7 @@ def test_comparison_orders(self):
         self.assertEqual(op_sequence(le, B, C), ['C.__ge__', 'B.__le__'])
         self.assertEqual(op_sequence(le, C, B), ['C.__le__', 'B.__ge__'])
 
-        self.assertTrue(issubclass(V, B))
+        self.assertIsSubclass(V, B)
         self.assertEqual(op_sequence(eq, B, V), ['B.__eq__', 'V.__eq__'])
         self.assertEqual(op_sequence(le, B, V), ['B.__le__', 'V.__ge__'])
 
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index 61921e9..19582e7 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -2879,11 +2879,11 @@ def test_memoryview_tolist(self):
     def test_memoryview_repr(self):
         m = memoryview(bytearray(9))
         r = m.__repr__()
-        self.assertTrue(r.startswith("<memory"))
+        self.assertStartsWith(r, "<memory")
 
         m.release()
         r = m.__repr__()
-        self.assertTrue(r.startswith("<released"))
+        self.assertStartsWith(r, "<released")
 
     def test_memoryview_sequence(self):
 
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index f1f877c..d221aa5 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -393,7 +393,7 @@ def test_chr(self):
         self.assertRaises(ValueError, chr, -2**1000)
 
     def test_cmp(self):
-        self.assertTrue(not hasattr(builtins, "cmp"))
+        self.assertNotHasAttr(builtins, "cmp")
 
     def test_compile(self):
         compile('print(1)\n', '', 'exec')
@@ -2304,7 +2304,7 @@ def __format__(self, format_spec):
         # tests for object.__format__ really belong elsewhere, but
         #  there's no good place to put them
         x = object().__format__('')
-        self.assertTrue(x.startswith('<object object at'))
+        self.assertStartsWith(x, '<object object at')
 
         # first argument to object.__format__ must be string
         self.assertRaises(TypeError, object().__format__, 3)
diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py
index 82d9916..bb0f8aa 100644
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -1974,9 +1974,9 @@ def test_compare_bytes_to_bytearray(self):
     @test.support.requires_docstrings
     def test_doc(self):
         self.assertIsNotNone(bytearray.__doc__)
-        self.assertTrue(bytearray.__doc__.startswith("bytearray("), bytearray.__doc__)
+        self.assertStartsWith(bytearray.__doc__, "bytearray(")
         self.assertIsNotNone(bytes.__doc__)
-        self.assertTrue(bytes.__doc__.startswith("bytes("), bytes.__doc__)
+        self.assertStartsWith(bytes.__doc__, "bytes(")
 
     def test_from_bytearray(self):
         sample = bytes(b"Hello world\n\x80\x81\xfe\xff")
@@ -2107,7 +2107,7 @@ class BytesAsStringTest(FixedStringTest, unittest.TestCase):
 class SubclassTest:
 
     def test_basic(self):
-        self.assertTrue(issubclass(self.type2test, self.basetype))
+        self.assertIsSubclass(self.type2test, self.basetype)
         self.assertIsInstance(self.type2test(), self.basetype)
 
         a, b = b"abcd", b"efgh"
@@ -2155,7 +2155,7 @@ def test_pickle(self):
             self.assertEqual(a.z, b.z)
             self.assertEqual(type(a), type(b))
             self.assertEqual(type(a.z), type(b.z))
-            self.assertFalse(hasattr(b, 'y'))
+            self.assertNotHasAttr(b, 'y')
 
     def test_copy(self):
         a = self.type2test(b"abcd")
@@ -2169,7 +2169,7 @@ def test_copy(self):
             self.assertEqual(a.z, b.z)
             self.assertEqual(type(a), type(b))
             self.assertEqual(type(a.z), type(b.z))
-            self.assertFalse(hasattr(b, 'y'))
+            self.assertNotHasAttr(b, 'y')
 
     def test_fromhex(self):
         b = self.type2test.fromhex('1a2B30')
diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py
index f32b24b..3b7897b 100644
--- a/Lib/test/test_bz2.py
+++ b/Lib/test/test_bz2.py
@@ -184,7 +184,7 @@ def testPeek(self):
         with BZ2File(self.filename) as bz2f:
             pdata = bz2f.peek()
             self.assertNotEqual(len(pdata), 0)
-            self.assertTrue(self.TEXT.startswith(pdata))
+            self.assertStartsWith(self.TEXT, pdata)
             self.assertEqual(bz2f.read(), self.TEXT)
 
     def testReadInto(self):
@@ -768,7 +768,7 @@ def testPeekBytesIO(self):
             with BZ2File(bio) as bz2f:
                 pdata = bz2f.peek()
                 self.assertNotEqual(len(pdata), 0)
-                self.assertTrue(self.TEXT.startswith(pdata))
+                self.assertStartsWith(self.TEXT, pdata)
                 self.assertEqual(bz2f.read(), self.TEXT)
 
     def testWriteBytesIO(self):
diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py
index cbfee60..7ade427 100644
--- a/Lib/test/test_calendar.py
+++ b/Lib/test/test_calendar.py
@@ -1098,7 +1098,7 @@ def test_option_type(self):
             output = run('--type', 'text', '2004')
             self.assertEqual(output, conv(result_2004_text))
             output = run('--type', 'html', '2004')
-            self.assertEqual(output[:6], b'<?xml ')
+            self.assertStartsWith(output, b'<?xml ')
             self.assertIn(b'<title>Calendar for 2004</title>', output)
 
     def test_html_output_current_year(self):
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 185ae84..1c73aaa 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -695,8 +695,8 @@ class DerivedType(SuperType):
         UnaffectedType2 = _testcapi.make_vectorcall_class(SuperType)
 
         # Aside: Quickly check that the C helper actually made derived types
-        self.assertTrue(issubclass(UnaffectedType1, DerivedType))
-        self.assertTrue(issubclass(UnaffectedType2, SuperType))
+        self.assertIsSubclass(UnaffectedType1, DerivedType)
+        self.assertIsSubclass(UnaffectedType2, SuperType)
 
         # Initial state: tp_call
         self.assertEqual(instance(), "tp_call")
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index f7fc3b3..580d54e 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -238,11 +238,11 @@ def test_directive_output_print(self):
         # The generated output will differ for every run, but we can check that
         # it starts with the clinic block, we check that it contains all the
         # expected fields, and we check that it contains the checksum line.
-        self.assertTrue(out.startswith(dedent("""
+        self.assertStartsWith(out, dedent("""
             /*[clinic input]
             output print 'I told you once.'
             [clinic start generated code]*/
-        """)))
+        """))
         fields = {
             "cpp_endif",
             "cpp_if",
@@ -259,9 +259,7 @@ def test_directive_output_print(self):
             with self.subTest(field=field):
                 self.assertIn(field, out)
         last_line = out.rstrip().split("\n")[-1]
-        self.assertTrue(
-            last_line.startswith("/*[clinic end generated code: output=")
-        )
+        self.assertStartsWith(last_line, "/*[clinic end generated code: output=")
 
     def test_directive_wrong_arg_number(self):
         raw = dedent("""
@@ -2705,8 +2703,7 @@ def test_cli_force(self):
             # Note, we cannot check the entire fail msg, because the path to
             # the tmp file will change for every run.
             _, err = self.expect_failure(fn)
-            self.assertTrue(err.endswith(fail_msg),
-                            f"{err!r} does not end with {fail_msg!r}")
+            self.assertEndsWith(err, fail_msg)
             # Then, force regeneration; success expected.
             out = self.expect_success("-f", fn)
             self.assertEqual(out, "")
@@ -2717,8 +2714,7 @@ def test_cli_force(self):
             )
             with open(fn, encoding='utf-8') as f:
                 generated = f.read()
-            self.assertTrue(generated.endswith(checksum),
-                            (generated, checksum))
+            self.assertEndsWith(generated, checksum)
 
     def test_cli_make(self):
         c_code = dedent("""
@@ -2867,8 +2863,8 @@ def test_cli_converters(self):
         # param may change (it's a set, thus unordered). So, let's compare the
         # start and end of the expected output, and then assert that the
         # converters appear lined up in alphabetical order.
-        self.assertTrue(out.startswith(prelude), out)
-        self.assertTrue(out.endswith(finale), out)
+        self.assertStartsWith(out, prelude)
+        self.assertEndsWith(out, finale)
 
         out = out.removeprefix(prelude)
         out = out.removesuffix(finale)
@@ -2876,10 +2872,7 @@ def test_cli_converters(self):
         for converter, line in zip(expected_converters, lines):
             line = line.lstrip()
             with self.subTest(converter=converter):
-                self.assertTrue(
-                    line.startswith(converter),
-                    f"expected converter {converter!r}, got {line!r}"
-                )
+                self.assertStartsWith(line, converter)
 
     def test_cli_fail_converters_and_filename(self):
         _, err = self.expect_failure("--converters", "test.c")
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index f540973..c17d749 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -39,7 +39,8 @@ def test_directories(self):
 
     def verify_valid_flag(self, cmd_line):
         rc, out, err = assert_python_ok(cmd_line)
-        self.assertTrue(out == b'' or out.endswith(b'\n'))
+        if out != b'':
+            self.assertEndsWith(out, b'\n')
         self.assertNotIn(b'Traceback', out)
         self.assertNotIn(b'Traceback', err)
         return out
@@ -89,8 +90,8 @@ def test_version(self):
         version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii")
         for switch in '-V', '--version', '-VV':
             rc, out, err = assert_python_ok(switch)
-            self.assertFalse(err.startswith(version))
-            self.assertTrue(out.startswith(version))
+            self.assertNotStartsWith(err, version)
+            self.assertStartsWith(out, version)
 
     def test_verbose(self):
         # -v causes imports to write to stderr.  If the write to
@@ -380,7 +381,7 @@ def test_unbuffered_input(self):
         p.stdin.flush()
         data, rc = _kill_python_and_exit_code(p)
         self.assertEqual(rc, 0)
-        self.assertTrue(data.startswith(b'x'), data)
+        self.assertStartsWith(data, b'x')
 
     def test_large_PYTHONPATH(self):
         path1 = "ABCDE" * 100
@@ -1039,7 +1040,7 @@ def test_parsing_error(self):
                               stderr=subprocess.PIPE,
                               text=True)
         err_msg = "Unknown option: --unknown-option\nusage: "
-        self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr)
+        self.assertStartsWith(proc.stderr, err_msg)
         self.assertNotEqual(proc.returncode, 0)
 
     def test_int_max_str_digits(self):
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 53dc9b1..784c45a 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -553,9 +553,9 @@ def test_pep_409_verbiage(self):
             exitcode, stdout, stderr = assert_python_failure(script_name)
             text = stderr.decode('ascii').split('\n')
             self.assertEqual(len(text), 5)
-            self.assertTrue(text[0].startswith('Traceback'))
-            self.assertTrue(text[1].startswith('  File '))
-            self.assertTrue(text[3].startswith('NameError'))
+            self.assertStartsWith(text[0], 'Traceback')
+            self.assertStartsWith(text[1], '  File ')
+            self.assertStartsWith(text[3], 'NameError')
 
     def test_non_ascii(self):
         # Apple platforms deny the creation of a file with an invalid UTF-8 name.
@@ -708,9 +708,8 @@ def test_syntaxerror_does_not_crash(self):
             exitcode, stdout, stderr = assert_python_failure(script_name)
             text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read()
             # It used to crash in https://github.com/python/cpython/issues/111132
-            self.assertTrue(text.endswith(
-                'SyntaxError: nonlocal declaration not allowed at module level\n',
-            ), text)
+            self.assertEndsWith(text,
+                'SyntaxError: nonlocal declaration not allowed at module level\n')
 
     def test_consistent_sys_path_for_direct_execution(self):
         # This test case ensures that the following all give the same
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index 57fb130..3642b47 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -133,7 +133,7 @@ def test_unicode_error(self):
         output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
         output = output[output.index('(InteractiveConsole)'):]
         output = output[output.index('\n') + 1:]
-        self.assertTrue(output.startswith('UnicodeEncodeError: '), output)
+        self.assertStartsWith(output, 'UnicodeEncodeError: ')
         self.assertIs(self.sysmod.last_type, UnicodeEncodeError)
         self.assertIs(type(self.sysmod.last_value), UnicodeEncodeError)
         self.assertIsNone(self.sysmod.last_traceback)
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index 1d61364..8c9a097 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -3802,7 +3802,7 @@ def check_decode_strings(self, errors):
                     with self.assertRaises(RuntimeError) as cm:
                         self.decode(encoded, errors)
                     errmsg = str(cm.exception)
-                    self.assertTrue(errmsg.startswith("decode error: "), errmsg)
+                    self.assertStartsWith(errmsg, "decode error: ")
                 else:
                     decoded = self.decode(encoded, errors)
                     self.assertEqual(decoded, expected)
diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
index a580a24..8384c18 100644
--- a/Lib/test/test_compileall.py
+++ b/Lib/test/test_compileall.py
@@ -316,7 +316,7 @@ def _test_ddir_only(self, *, ddir, parallel=True):
 
         self.assertTrue(mods)
         for mod in mods:
-            self.assertTrue(mod.startswith(self.directory), mod)
+            self.assertStartsWith(mod, self.directory)
             modcode = importlib.util.cache_from_source(mod)
             modpath = mod[len(self.directory+os.sep):]
             _, _, err = script_helper.assert_python_failure(modcode)
diff --git a/Lib/test/test_compiler_assemble.py b/Lib/test/test_compiler_assemble.py
index c4962e3..99a11e9 100644
--- a/Lib/test/test_compiler_assemble.py
+++ b/Lib/test/test_compiler_assemble.py
@@ -146,4 +146,4 @@ def test_exception_table(self):
                                          L1 to L2 -> L2 [0]
                                          L2 to L3 -> L3 [1] lasti
                                     """)
-        self.assertTrue(output.getvalue().endswith(exc_table))
+        self.assertEndsWith(output.getvalue(), exc_table)
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py
index cf65195..6a3329f 100644
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -48,23 +48,23 @@ def __enter__(self):
             def __exit__(self, exc_type, exc_value, traceback):
                 return None
 
-        self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager))
+        self.assertIsSubclass(ManagerFromScratch, AbstractContextManager)
 
         class DefaultEnter(AbstractContextManager):
             def __exit__(self, *args):
                 super().__exit__(*args)
 
-        self.assertTrue(issubclass(DefaultEnter, AbstractContextManager))
+        self.assertIsSubclass(DefaultEnter, AbstractContextManager)
 
         class NoEnter(ManagerFromScratch):
             __enter__ = None
 
-        self.assertFalse(issubclass(NoEnter, AbstractContextManager))
+        self.assertNotIsSubclass(NoEnter, AbstractContextManager)
 
         class NoExit(ManagerFromScratch):
             __exit__ = None
 
-        self.assertFalse(issubclass(NoExit, AbstractContextManager))
+        self.assertNotIsSubclass(NoExit, AbstractContextManager)
 
 
 class ContextManagerTestCase(unittest.TestCase):
diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py
index 7750186..dcd0072 100644
--- a/Lib/test/test_contextlib_async.py
+++ b/Lib/test/test_contextlib_async.py
@@ -77,23 +77,23 @@ async def __aenter__(self):
             async def __aexit__(self, exc_type, exc_value, traceback):
                 return None
 
-        self.assertTrue(issubclass(ManagerFromScratch, AbstractAsyncContextManager))
+        self.assertIsSubclass(ManagerFromScratch, AbstractAsyncContextManager)
 
         class DefaultEnter(AbstractAsyncContextManager):
             async def __aexit__(self, *args):
                 await super().__aexit__(*args)
 
-        self.assertTrue(issubclass(DefaultEnter, AbstractAsyncContextManager))
+        self.assertIsSubclass(DefaultEnter, AbstractAsyncContextManager)
 
         class NoneAenter(ManagerFromScratch):
             __aenter__ = None
 
-        self.assertFalse(issubclass(NoneAenter, AbstractAsyncContextManager))
+        self.assertNotIsSubclass(NoneAenter, AbstractAsyncContextManager)
 
         class NoneAexit(ManagerFromScratch):
             __aexit__ = None
 
-        self.assertFalse(issubclass(NoneAexit, AbstractAsyncContextManager))
+        self.assertNotIsSubclass(NoneAexit, AbstractAsyncContextManager)
 
 
 class AsyncContextManagerTestCase(unittest.TestCase):
diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py
index d763414..3cb8072 100644
--- a/Lib/test/test_copy.py
+++ b/Lib/test/test_copy.py
@@ -19,7 +19,7 @@ class TestCopy(unittest.TestCase):
 
     def test_exceptions(self):
         self.assertIs(copy.Error, copy.error)
-        self.assertTrue(issubclass(copy.Error, Exception))
+        self.assertIsSubclass(copy.Error, Exception)
 
     # The copy() method
 
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 761cb23..4755046 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -527,7 +527,7 @@ class CoroutineTest(unittest.TestCase):
 
     def test_gen_1(self):
         def gen(): yield
-        self.assertFalse(hasattr(gen, '__await__'))
+        self.assertNotHasAttr(gen, '__await__')
 
     def test_func_1(self):
         async def foo():
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index feca826..d5ca7f2 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -1260,7 +1260,7 @@ class TestAscii(unittest.TestCase):
 
     def test_controlnames(self):
         for name in curses.ascii.controlnames:
-            self.assertTrue(hasattr(curses.ascii, name), name)
+            self.assertHasAttr(curses.ascii, name)
 
     def test_ctypes(self):
         def check(func, expected):
diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py
index ac78f83..e98a8f2 100644
--- a/Lib/test/test_dataclasses/__init__.py
+++ b/Lib/test/test_dataclasses/__init__.py
@@ -120,7 +120,7 @@ class Some: pass
         for param in inspect.signature(dataclass).parameters:
             if param == 'cls':
                 continue
-            self.assertTrue(hasattr(Some.__dataclass_params__, param), msg=param)
+            self.assertHasAttr(Some.__dataclass_params__, param)
 
     def test_named_init_params(self):
         @dataclass
@@ -671,7 +671,7 @@ class C:
 
         self.assertEqual(the_fields[0].name, 'x')
         self.assertEqual(the_fields[0].type, int)
-        self.assertFalse(hasattr(C, 'x'))
+        self.assertNotHasAttr(C, 'x')
         self.assertTrue (the_fields[0].init)
         self.assertTrue (the_fields[0].repr)
         self.assertEqual(the_fields[1].name, 'y')
@@ -681,7 +681,7 @@ class C:
         self.assertTrue (the_fields[1].repr)
         self.assertEqual(the_fields[2].name, 'z')
         self.assertEqual(the_fields[2].type, str)
-        self.assertFalse(hasattr(C, 'z'))
+        self.assertNotHasAttr(C, 'z')
         self.assertTrue (the_fields[2].init)
         self.assertFalse(the_fields[2].repr)
 
@@ -732,8 +732,8 @@ class C:
             z: object = default
             t: int = field(default=100)
 
-        self.assertFalse(hasattr(C, 'x'))
-        self.assertFalse(hasattr(C, 'y'))
+        self.assertNotHasAttr(C, 'x')
+        self.assertNotHasAttr(C, 'y')
         self.assertIs   (C.z, default)
         self.assertEqual(C.t, 100)
 
@@ -2912,10 +2912,10 @@ class C:
             pass
 
         c = C()
-        self.assertFalse(hasattr(c, 'i'))
+        self.assertNotHasAttr(c, 'i')
         with self.assertRaises(FrozenInstanceError):
             c.i = 5
-        self.assertFalse(hasattr(c, 'i'))
+        self.assertNotHasAttr(c, 'i')
         with self.assertRaises(FrozenInstanceError):
             del c.i
 
@@ -3144,7 +3144,7 @@ class S(D):
             del s.y
         self.assertEqual(s.y, 10)
         del s.cached
-        self.assertFalse(hasattr(s, 'cached'))
+        self.assertNotHasAttr(s, 'cached')
         with self.assertRaises(AttributeError) as cm:
             del s.cached
         self.assertNotIsInstance(cm.exception, FrozenInstanceError)
@@ -3158,12 +3158,12 @@ class S(D):
             pass
 
         s = S()
-        self.assertFalse(hasattr(s, 'x'))
+        self.assertNotHasAttr(s, 'x')
         s.x = 5
         self.assertEqual(s.x, 5)
 
         del s.x
-        self.assertFalse(hasattr(s, 'x'))
+        self.assertNotHasAttr(s, 'x')
         with self.assertRaises(AttributeError) as cm:
             del s.x
         self.assertNotIsInstance(cm.exception, FrozenInstanceError)
@@ -3393,8 +3393,8 @@ class A:
         B = dataclass(A, slots=True)
         self.assertIsNot(A, B)
 
-        self.assertFalse(hasattr(A, "__slots__"))
-        self.assertTrue(hasattr(B, "__slots__"))
+        self.assertNotHasAttr(A, "__slots__")
+        self.assertHasAttr(B, "__slots__")
 
     # Can't be local to test_frozen_pickle.
     @dataclass(frozen=True, slots=True)
diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py
index 4be7c56..a10922a 100644
--- a/Lib/test/test_dbm.py
+++ b/Lib/test/test_dbm.py
@@ -66,7 +66,7 @@ def keys_helper(self, f):
         return keys
 
     def test_error(self):
-        self.assertTrue(issubclass(self.module.error, OSError))
+        self.assertIsSubclass(self.module.error, OSError)
 
     def test_anydbm_not_existing(self):
         self.assertRaises(dbm.error, dbm.open, _fname)
diff --git a/Lib/test/test_dbm_sqlite3.py b/Lib/test/test_dbm_sqlite3.py
index 2e1f2d3..9216da8 100644
--- a/Lib/test/test_dbm_sqlite3.py
+++ b/Lib/test/test_dbm_sqlite3.py
@@ -36,7 +36,7 @@ def test_uri_substitutions(self):
         )
         for path, normalized in dataset:
             with self.subTest(path=path, normalized=normalized):
-                self.assertTrue(_normalize_uri(path).endswith(normalized))
+                self.assertEndsWith(_normalize_uri(path), normalized)
 
     @unittest.skipUnless(sys.platform == "win32", "requires Windows")
     def test_uri_windows(self):
@@ -55,7 +55,7 @@ def test_uri_windows(self):
             with self.subTest(path=path, normalized=normalized):
                 if not Path(path).is_absolute():
                     self.skipTest(f"skipping relative path: {path!r}")
-                self.assertTrue(_normalize_uri(path).endswith(normalized))
+                self.assertEndsWith(_normalize_uri(path), normalized)
 
 
 class ReadOnly(_SQLiteDbmTests):
diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py
index 4679f29..4e1a489 100644
--- a/Lib/test/test_deque.py
+++ b/Lib/test/test_deque.py
@@ -838,7 +838,7 @@ def test_copy_pickle(self):
                     self.assertEqual(list(d), list(e))
                     self.assertEqual(e.x, d.x)
                     self.assertEqual(e.z, d.z)
-                    self.assertFalse(hasattr(e, 'y'))
+                    self.assertNotHasAttr(e, 'y')
 
     def test_pickle_recursive(self):
         for proto in range(pickle.HIGHEST_PROTOCOL + 1):
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 1402653..d635855 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -409,7 +409,7 @@ class ClassPropertiesAndMethods(unittest.TestCase):
 
     def test_python_dicts(self):
         # Testing Python subclass of dict...
-        self.assertTrue(issubclass(dict, dict))
+        self.assertIsSubclass(dict, dict)
         self.assertIsInstance({}, dict)
         d = dict()
         self.assertEqual(d, {})
@@ -433,7 +433,7 @@ def setstate(self, state):
                 self.state = state
             def getstate(self):
                 return self.state
-        self.assertTrue(issubclass(C, dict))
+        self.assertIsSubclass(C, dict)
         a1 = C(12)
         self.assertEqual(a1.state, 12)
         a2 = C(foo=1, bar=2)
@@ -1048,15 +1048,15 @@ class SubType(types.ModuleType):
 
         m = types.ModuleType("m")
         self.assertTrue(m.__class__ is types.ModuleType)
-        self.assertFalse(hasattr(m, "a"))
+        self.assertNotHasAttr(m, "a")
 
         m.__class__ = SubType
         self.assertTrue(m.__class__ is SubType)
-        self.assertTrue(hasattr(m, "a"))
+        self.assertHasAttr(m, "a")
 
         m.__class__ = types.ModuleType
         self.assertTrue(m.__class__ is types.ModuleType)
-        self.assertFalse(hasattr(m, "a"))
+        self.assertNotHasAttr(m, "a")
 
         # Make sure that builtin immutable objects don't support __class__
         # assignment, because the object instances may be interned.
@@ -1780,7 +1780,7 @@ class D(C):
         class E: # *not* subclassing from C
             foo = C.foo
         self.assertEqual(E().foo.__func__, C.foo) # i.e., unbound
-        self.assertTrue(repr(C.foo.__get__(C())).startswith("<bound method "))
+        self.assertStartsWith(repr(C.foo.__get__(C())), "<bound method ")
 
     def test_compattr(self):
         # Testing computed attributes...
@@ -2058,7 +2058,7 @@ class D(C):
         class E(object):
             foo = C.foo
         self.assertEqual(E().foo.__func__, C.foo) # i.e., unbound
-        self.assertTrue(repr(C.foo.__get__(C(1))).startswith("<bound method "))
+        self.assertStartsWith(repr(C.foo.__get__(C(1))), "<bound method ")
 
     @support.impl_detail("testing error message from implementation")
     def test_methods_in_c(self):
@@ -5195,8 +5195,8 @@ def test_repr(self):
         # We can't blindly compare with the repr of another dict as ordering
         # of keys and values is arbitrary and may differ.
         r = repr(self.C.__dict__)
-        self.assertTrue(r.startswith('mappingproxy('), r)
-        self.assertTrue(r.endswith(')'), r)
+        self.assertStartsWith(r, 'mappingproxy(')
+        self.assertEndsWith(r, ')')
         for k, v in self.C.__dict__.items():
             self.assertIn('{!r}: {!r}'.format(k, v), r)
 
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 10a6d07..52c38e4 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -787,8 +787,8 @@ def test_dictview_mixed_set_operations(self):
 
     def test_missing(self):
         # Make sure dict doesn't have a __missing__ method
-        self.assertFalse(hasattr(dict, "__missing__"))
-        self.assertFalse(hasattr({}, "__missing__"))
+        self.assertNotHasAttr(dict, "__missing__")
+        self.assertNotHasAttr({}, "__missing__")
         # Test several cases:
         # (D) subclass defines __missing__ method returning a value
         # (E) subclass defines __missing__ method raising RuntimeError
diff --git a/Lib/test/test_dynamicclassattribute.py b/Lib/test/test_dynamicclassattribute.py
index 9f694d9..b19be33 100644
--- a/Lib/test/test_dynamicclassattribute.py
+++ b/Lib/test/test_dynamicclassattribute.py
@@ -104,8 +104,8 @@ def test_property_decorator_baseclass(self):
         self.assertEqual(base.spam, 10)
         self.assertEqual(base._spam, 10)
         delattr(base, "spam")
-        self.assertTrue(not hasattr(base, "spam"))
-        self.assertTrue(not hasattr(base, "_spam"))
+        self.assertNotHasAttr(base, "spam")
+        self.assertNotHasAttr(base, "_spam")
         base.spam = 20
         self.assertEqual(base.spam, 20)
         self.assertEqual(base._spam, 20)
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index d8cb526..221f9db 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -434,9 +434,9 @@ class Season(self.enum_type):
             def spam(cls):
                 pass
         #
-        self.assertTrue(hasattr(Season, 'spam'))
+        self.assertHasAttr(Season, 'spam')
         del Season.spam
-        self.assertFalse(hasattr(Season, 'spam'))
+        self.assertNotHasAttr(Season, 'spam')
         #
         with self.assertRaises(AttributeError):
             del Season.SPRING
@@ -2652,12 +2652,12 @@ def __new__(cls, value, period):
             OneDay = day_1
             OneWeek = week_1
             OneMonth = month_1
-        self.assertFalse(hasattr(Period, '_ignore_'))
-        self.assertFalse(hasattr(Period, 'Period'))
-        self.assertFalse(hasattr(Period, 'i'))
-        self.assertTrue(isinstance(Period.day_1, timedelta))
-        self.assertTrue(Period.month_1 is Period.day_30)
-        self.assertTrue(Period.week_4 is Period.day_28)
+        self.assertNotHasAttr(Period, '_ignore_')
+        self.assertNotHasAttr(Period, 'Period')
+        self.assertNotHasAttr(Period, 'i')
+        self.assertIsInstance(Period.day_1, timedelta)
+        self.assertIs(Period.month_1, Period.day_30)
+        self.assertIs(Period.week_4, Period.day_28)
 
     def test_nonhash_value(self):
         class AutoNumberInAList(Enum):
@@ -2877,7 +2877,7 @@ class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum):
         self.assertEqual(str(ReformedColor.BLUE), 'blue')
         self.assertEqual(ReformedColor.RED.behavior(), 'booyah')
         self.assertEqual(ConfusedColor.RED.social(), "what's up?")
-        self.assertTrue(issubclass(ReformedColor, int))
+        self.assertIsSubclass(ReformedColor, int)
 
     def test_multiple_inherited_mixin(self):
         @unique
diff --git a/Lib/test/test_errno.py b/Lib/test/test_errno.py
index 5c437e9..e7f185c 100644
--- a/Lib/test/test_errno.py
+++ b/Lib/test/test_errno.py
@@ -12,14 +12,12 @@ class ErrnoAttributeTests(unittest.TestCase):
     def test_for_improper_attributes(self):
         # No unexpected attributes should be on the module.
         for error_code in std_c_errors:
-            self.assertTrue(hasattr(errno, error_code),
-                            "errno is missing %s" % error_code)
+            self.assertHasAttr(errno, error_code)
 
     def test_using_errorcode(self):
         # Every key value in errno.errorcode should be on the module.
         for value in errno.errorcode.values():
-            self.assertTrue(hasattr(errno, value),
-                            'no %s attr in errno' % value)
+            self.assertHasAttr(errno, value)
 
 
 class ErrorcodeTests(unittest.TestCase):
diff --git a/Lib/test/test_exception_group.py b/Lib/test/test_exception_group.py
index 92bbf79..242d7ce 100644
--- a/Lib/test/test_exception_group.py
+++ b/Lib/test/test_exception_group.py
@@ -5,9 +5,9 @@
 
 class TestExceptionGroupTypeHierarchy(unittest.TestCase):
     def test_exception_group_types(self):
-        self.assertTrue(issubclass(ExceptionGroup, Exception))
-        self.assertTrue(issubclass(ExceptionGroup, BaseExceptionGroup))
-        self.assertTrue(issubclass(BaseExceptionGroup, BaseException))
+        self.assertIsSubclass(ExceptionGroup, Exception)
+        self.assertIsSubclass(ExceptionGroup, BaseExceptionGroup)
+        self.assertIsSubclass(BaseExceptionGroup, BaseException)
 
     def test_exception_is_not_generic_type(self):
         with self.assertRaisesRegex(TypeError, 'Exception'):
@@ -812,8 +812,8 @@ def test_split_does_not_copy_non_sequence_notes(self):
         eg = ExceptionGroup("eg", [ValueError(1), TypeError(2)])
         eg.__notes__ = 123
         match, rest = eg.split(TypeError)
-        self.assertFalse(hasattr(match, '__notes__'))
-        self.assertFalse(hasattr(rest, '__notes__'))
+        self.assertNotHasAttr(match, '__notes__')
+        self.assertNotHasAttr(rest, '__notes__')
 
     def test_drive_invalid_return_value(self):
         class MyEg(ExceptionGroup):
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index cfd4a3e..175ef53 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -357,7 +357,7 @@ def test_capi1():
             except TypeError as err:
                 co = err.__traceback__.tb_frame.f_code
                 self.assertEqual(co.co_name, "test_capi1")
-                self.assertTrue(co.co_filename.endswith('test_exceptions.py'))
+                self.assertEndsWith(co.co_filename, 'test_exceptions.py')
             else:
                 self.fail("Expected exception")
 
@@ -369,7 +369,7 @@ def test_capi2():
                 tb = err.__traceback__.tb_next
                 co = tb.tb_frame.f_code
                 self.assertEqual(co.co_name, "__init__")
-                self.assertTrue(co.co_filename.endswith('test_exceptions.py'))
+                self.assertEndsWith(co.co_filename, 'test_exceptions.py')
                 co2 = tb.tb_frame.f_back.f_code
                 self.assertEqual(co2.co_name, "test_capi2")
             else:
@@ -598,7 +598,7 @@ def test_invalid_setstate(self):
     def test_notes(self):
         for e in [BaseException(1), Exception(2), ValueError(3)]:
             with self.subTest(e=e):
-                self.assertFalse(hasattr(e, '__notes__'))
+                self.assertNotHasAttr(e, '__notes__')
                 e.add_note("My Note")
                 self.assertEqual(e.__notes__, ["My Note"])
 
@@ -610,7 +610,7 @@ def test_notes(self):
                 self.assertEqual(e.__notes__, ["My Note", "Your Note"])
 
                 del e.__notes__
-                self.assertFalse(hasattr(e, '__notes__'))
+                self.assertNotHasAttr(e, '__notes__')
 
                 e.add_note("Our Note")
                 self.assertEqual(e.__notes__, ["Our Note"])
@@ -1627,7 +1627,7 @@ def test_exception_with_doc(self):
         # test basic usage of PyErr_NewException
         error1 = _testcapi.make_exception_with_doc("_testcapi.error1")
         self.assertIs(type(error1), type)
-        self.assertTrue(issubclass(error1, Exception))
+        self.assertIsSubclass(error1, Exception)
         self.assertIsNone(error1.__doc__)
 
         # test with given docstring
@@ -1637,21 +1637,21 @@ def test_exception_with_doc(self):
         # test with explicit base (without docstring)
         error3 = _testcapi.make_exception_with_doc("_testcapi.error3",
                                                    base=error2)
-        self.assertTrue(issubclass(error3, error2))
+        self.assertIsSubclass(error3, error2)
 
         # test with explicit base tuple
         class C(object):
             pass
         error4 = _testcapi.make_exception_with_doc("_testcapi.error4", doc4,
                                                    (error3, C))
-        self.assertTrue(issubclass(error4, error3))
-        self.assertTrue(issubclass(error4, C))
+        self.assertIsSubclass(error4, error3)
+        self.assertIsSubclass(error4, C)
         self.assertEqual(error4.__doc__, doc4)
 
         # test with explicit dictionary
         error5 = _testcapi.make_exception_with_doc("_testcapi.error5", "",
                                                    error4, {'a': 1})
-        self.assertTrue(issubclass(error5, error4))
+        self.assertIsSubclass(error5, error4)
         self.assertEqual(error5.a, 1)
         self.assertEqual(error5.__doc__, "")
 
@@ -1744,7 +1744,7 @@ def test_unhandled(self):
                     self.assertIn("<exception str() failed>", report)
                 else:
                     self.assertIn("test message", report)
-                self.assertTrue(report.endswith("\n"))
+                self.assertEndsWith(report, "\n")
 
     @cpython_only
     # Python built with Py_TRACE_REFS fail with a fatal error in
diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py
index b340ef7..6524baa 100644
--- a/Lib/test/test_fileinput.py
+++ b/Lib/test/test_fileinput.py
@@ -245,7 +245,7 @@ def test_detached_stdin_binary_mode(self):
         orig_stdin = sys.stdin
         try:
             sys.stdin = BytesIO(b'spam, bacon, sausage, and spam')
-            self.assertFalse(hasattr(sys.stdin, 'buffer'))
+            self.assertNotHasAttr(sys.stdin, 'buffer')
             fi = FileInput(files=['-'], mode='rb')
             lines = list(fi)
             self.assertEqual(lines, [b'spam, bacon, sausage, and spam'])
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 95c98c6..b4cbfb6 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -914,7 +914,7 @@ def __del__(self):
         gc.collect()
         self.assertEqual(len(Lazarus.resurrected_instances), 1)
         instance = Lazarus.resurrected_instances.pop()
-        self.assertTrue(hasattr(instance, "cargo"))
+        self.assertHasAttr(instance, "cargo")
         self.assertEqual(id(instance.cargo), cargo_id)
 
         gc.collect()
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index ea0dc24..7601cb0 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -236,13 +236,13 @@ class MyGeneric:
         self.assertEqual(repr(x2), 'tuple[*tuple[int, str]]')
         x3 = tuple[*tuple[int, ...]]
         self.assertEqual(repr(x3), 'tuple[*tuple[int, ...]]')
-        self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
+        self.assertEndsWith(repr(MyList[int]), '.BaseTest.test_repr.<locals>.MyList[int]')
         self.assertEqual(repr(list[str]()), '[]')  # instances should keep their normal repr
 
         # gh-105488
-        self.assertTrue(repr(MyGeneric[int]).endswith('MyGeneric[int]'))
-        self.assertTrue(repr(MyGeneric[[]]).endswith('MyGeneric[[]]'))
-        self.assertTrue(repr(MyGeneric[[int, str]]).endswith('MyGeneric[[int, str]]'))
+        self.assertEndsWith(repr(MyGeneric[int]), 'MyGeneric[int]')
+        self.assertEndsWith(repr(MyGeneric[[]]), 'MyGeneric[[]]')
+        self.assertEndsWith(repr(MyGeneric[[int, str]]), 'MyGeneric[[int, str]]')
 
     def test_exposed_type(self):
         import types
@@ -362,7 +362,7 @@ def test_isinstance(self):
 
     def test_issubclass(self):
         class L(list): ...
-        self.assertTrue(issubclass(L, list))
+        self.assertIsSubclass(L, list)
         with self.assertRaises(TypeError):
             issubclass(L, list[str])
 
diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py
index 6c3abe6..df07af0 100644
--- a/Lib/test/test_genericpath.py
+++ b/Lib/test/test_genericpath.py
@@ -92,8 +92,8 @@ def test_commonprefix(self):
         for s1 in testlist:
             for s2 in testlist:
                 p = commonprefix([s1, s2])
-                self.assertTrue(s1.startswith(p))
-                self.assertTrue(s2.startswith(p))
+                self.assertStartsWith(s1, p)
+                self.assertStartsWith(s2, p)
                 if s1 != s2:
                     n = len(p)
                     self.assertNotEqual(s1[n:n+1], s2[n:n+1])
diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py
index fa5de7c..ccbacc7 100644
--- a/Lib/test/test_gzip.py
+++ b/Lib/test/test_gzip.py
@@ -331,13 +331,13 @@ def test_mode(self):
     def test_1647484(self):
         for mode in ('wb', 'rb'):
             with gzip.GzipFile(self.filename, mode) as f:
-                self.assertTrue(hasattr(f, "name"))
+                self.assertHasAttr(f, "name")
                 self.assertEqual(f.name, self.filename)
 
     def test_paddedfile_getattr(self):
         self.test_write()
         with gzip.GzipFile(self.filename, 'rb') as f:
-            self.assertTrue(hasattr(f.fileobj, "name"))
+            self.assertHasAttr(f.fileobj, "name")
             self.assertEqual(f.fileobj.name, self.filename)
 
     def test_mtime(self):
@@ -345,7 +345,7 @@ def test_mtime(self):
         with gzip.GzipFile(self.filename, 'w', mtime = mtime) as fWrite:
             fWrite.write(data1)
         with gzip.GzipFile(self.filename) as fRead:
-            self.assertTrue(hasattr(fRead, 'mtime'))
+            self.assertHasAttr(fRead, 'mtime')
             self.assertIsNone(fRead.mtime)
             dataRead = fRead.read()
             self.assertEqual(dataRead, data1)
@@ -460,7 +460,7 @@ def test_zero_padded_file(self):
             self.assertEqual(d, data1 * 50, "Incorrect data in file")
 
     def test_gzip_BadGzipFile_exception(self):
-        self.assertTrue(issubclass(gzip.BadGzipFile, OSError))
+        self.assertIsSubclass(gzip.BadGzipFile, OSError)
 
     def test_bad_gzip_file(self):
         with open(self.filename, 'wb') as file:
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 5e3356a..de4c8a1 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -152,8 +152,8 @@ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs):
         if _hashlib:
             # These algorithms should always be present when this module
             # is compiled.  If not, something was compiled wrong.
-            self.assertTrue(hasattr(_hashlib, 'openssl_md5'))
-            self.assertTrue(hasattr(_hashlib, 'openssl_sha1'))
+            self.assertHasAttr(_hashlib, 'openssl_md5')
+            self.assertHasAttr(_hashlib, 'openssl_sha1')
             for algorithm, constructors in self.constructors_to_test.items():
                 constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
                 if constructor:
diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py
index 1c325c0..e584fb4 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -786,12 +786,12 @@ def test_getfile(self):
     def test_getfile_builtin_module(self):
         with self.assertRaises(TypeError) as e:
             inspect.getfile(sys)
-        self.assertTrue(str(e.exception).startswith('<module'))
+        self.assertStartsWith(str(e.exception), '<module')
 
     def test_getfile_builtin_class(self):
         with self.assertRaises(TypeError) as e:
             inspect.getfile(int)
-        self.assertTrue(str(e.exception).startswith('<class'))
+        self.assertStartsWith(str(e.exception), '<class')
 
     def test_getfile_builtin_function_or_method(self):
         with self.assertRaises(TypeError) as e_abs:
@@ -2949,7 +2949,7 @@ def test(po, /, pk, pkd=100, *args, ko, kod=10, **kwargs):
             pass
 
         sig = inspect.signature(test)
-        self.assertTrue(repr(sig).startswith('<Signature'))
+        self.assertStartsWith(repr(sig), '<Signature')
         self.assertTrue('(po, /, pk' in repr(sig))
 
         # We need two functions, because it is impossible to represent
@@ -2958,7 +2958,7 @@ def test2(pod=42, /):
             pass
 
         sig2 = inspect.signature(test2)
-        self.assertTrue(repr(sig2).startswith('<Signature'))
+        self.assertStartsWith(repr(sig2), '<Signature')
         self.assertTrue('(pod=42, /)' in repr(sig2))
 
         po = sig.parameters['po']
@@ -5133,7 +5133,7 @@ def test_signature_parameter_object(self):
         with self.assertRaisesRegex(ValueError, 'cannot have default values'):
             p.replace(kind=inspect.Parameter.VAR_POSITIONAL)
 
-        self.assertTrue(repr(p).startswith('<Parameter'))
+        self.assertStartsWith(repr(p), '<Parameter')
         self.assertTrue('"a=42"' in repr(p))
 
     def test_signature_parameter_hashable(self):
diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py
index 245528c..7a7cb73 100644
--- a/Lib/test/test_int.py
+++ b/Lib/test/test_int.py
@@ -836,7 +836,7 @@ def test_pylong_roundtrip(self):
             n = hibit | getrandbits(bits - 1)
             assert n.bit_length() == bits
             sn = str(n)
-            self.assertFalse(sn.startswith('0'))
+            self.assertNotStartsWith(sn, '0')
             self.assertEqual(n, int(sn))
             bits <<= 1
 
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index aa619a9..168e66c 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -902,7 +902,7 @@ def test_types_have_dict(self):
             self.BytesIO()
         )
         for obj in test:
-            self.assertTrue(hasattr(obj, "__dict__"))
+            self.assertHasAttr(obj, "__dict__")
 
     def test_opener(self):
         with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f:
@@ -1117,7 +1117,7 @@ def test_class_hierarchy(self):
         def check_subs(types, base):
             for tp in types:
                 with self.subTest(tp=tp, base=base):
-                    self.assertTrue(issubclass(tp, base))
+                    self.assertIsSubclass(tp, base)
 
         def recursive_check(d):
             for k, v in d.items():
@@ -1870,7 +1870,7 @@ def test_write_overflow(self):
         flushed = b"".join(writer._write_stack)
         # At least (total - 8) bytes were implicitly flushed, perhaps more
         # depending on the implementation.
-        self.assertTrue(flushed.startswith(contents[:-8]), flushed)
+        self.assertStartsWith(flushed, contents[:-8])
 
     def check_writes(self, intermediate_func):
         # Lots of writes, test the flushed output is as expected.
@@ -1940,7 +1940,7 @@ def test_write_non_blocking(self):
         self.assertEqual(bufio.write(b"ABCDEFGHI"), 9)
         s = raw.pop_written()
         # Previously buffered bytes were flushed
-        self.assertTrue(s.startswith(b"01234567A"), s)
+        self.assertStartsWith(s, b"01234567A")
 
     def test_write_and_rewind(self):
         raw = self.BytesIO()
@@ -2236,7 +2236,7 @@ def test_write(self):
     def test_peek(self):
         pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO())
 
-        self.assertTrue(pair.peek(3).startswith(b"abc"))
+        self.assertStartsWith(pair.peek(3), b"abc")
         self.assertEqual(pair.read(3), b"abc")
 
     def test_readable(self):
@@ -4618,10 +4618,8 @@ def test_check_encoding_warning(self):
         proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code)
         warnings = proc.err.splitlines()
         self.assertEqual(len(warnings), 2)
-        self.assertTrue(
-            warnings[0].startswith(b"<string>:5: EncodingWarning: "))
-        self.assertTrue(
-            warnings[1].startswith(b"<string>:8: EncodingWarning: "))
+        self.assertStartsWith(warnings[0], b"<string>:5: EncodingWarning: ")
+        self.assertStartsWith(warnings[1], b"<string>:8: EncodingWarning: ")
 
     def test_text_encoding(self):
         # PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8"
@@ -4834,7 +4832,7 @@ def on_alarm(*args):
                     os.read(r, len(data) * 100)
             exc = cm.exception
             if isinstance(exc, RuntimeError):
-                self.assertTrue(str(exc).startswith("reentrant call"), str(exc))
+                self.assertStartsWith(str(exc), "reentrant call")
         finally:
             signal.alarm(0)
             wio.close()
diff --git a/Lib/test/test_json/test_fail.py b/Lib/test/test_json/test_fail.py
index 7c1696c..79c44af 100644
--- a/Lib/test/test_json/test_fail.py
+++ b/Lib/test/test_json/test_fail.py
@@ -102,7 +102,7 @@ def test_not_serializable(self):
         with self.assertRaisesRegex(TypeError,
                 'Object of type module is not JSON serializable') as cm:
             self.dumps(sys)
-        self.assertFalse(hasattr(cm.exception, '__notes__'))
+        self.assertNotHasAttr(cm.exception, '__notes__')
 
         with self.assertRaises(TypeError) as cm:
             self.dumps([1, [2, 3, sys]])
diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py
index 72cde3f..9ea2679 100644
--- a/Lib/test/test_json/test_tool.py
+++ b/Lib/test/test_json/test_tool.py
@@ -160,7 +160,7 @@ def test_help_flag(self):
         rc, out, err = assert_python_ok('-m', self.module, '-h',
                                         PYTHON_COLORS='0')
         self.assertEqual(rc, 0)
-        self.assertTrue(out.startswith(b'usage: '))
+        self.assertStartsWith(out, b'usage: ')
         self.assertEqual(err, b'')
 
     def test_sort_keys_flag(self):
diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py
index 173fc74..caa1603 100644
--- a/Lib/test/test_launcher.py
+++ b/Lib/test/test_launcher.py
@@ -443,7 +443,7 @@ def test_search_major_3(self):
         except subprocess.CalledProcessError:
             raise unittest.SkipTest("requires at least one Python 3.x install")
         self.assertEqual("PythonCore", data["env.company"])
-        self.assertTrue(data["env.tag"].startswith("3."), data["env.tag"])
+        self.assertStartsWith(data["env.tag"], "3.")
 
     def test_search_major_3_32(self):
         try:
@@ -453,8 +453,8 @@ def test_search_major_3_32(self):
                 raise unittest.SkipTest("requires at least one 32-bit Python 3.x install")
             raise
         self.assertEqual("PythonCore", data["env.company"])
-        self.assertTrue(data["env.tag"].startswith("3."), data["env.tag"])
-        self.assertTrue(data["env.tag"].endswith("-32"), data["env.tag"])
+        self.assertStartsWith(data["env.tag"], "3.")
+        self.assertEndsWith(data["env.tag"], "-32")
 
     def test_search_major_2(self):
         try:
@@ -463,7 +463,7 @@ def test_search_major_2(self):
             if not is_installed("2.7"):
                 raise unittest.SkipTest("requires at least one Python 2.x install")
         self.assertEqual("PythonCore", data["env.company"])
-        self.assertTrue(data["env.tag"].startswith("2."), data["env.tag"])
+        self.assertStartsWith(data["env.tag"], "2.")
 
     def test_py_default(self):
         with self.py_ini(TEST_PY_DEFAULTS):
diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py
index 9ffb93e..e93c3c3 100644
--- a/Lib/test/test_lzma.py
+++ b/Lib/test/test_lzma.py
@@ -1025,12 +1025,12 @@ def test_peek(self):
         with LZMAFile(BytesIO(COMPRESSED_XZ)) as f:
             result = f.peek()
             self.assertGreater(len(result), 0)
-            self.assertTrue(INPUT.startswith(result))
+            self.assertStartsWith(INPUT, result)
             self.assertEqual(f.read(), INPUT)
         with LZMAFile(BytesIO(COMPRESSED_XZ)) as f:
             result = f.peek(10)
             self.assertGreater(len(result), 0)
-            self.assertTrue(INPUT.startswith(result))
+            self.assertStartsWith(INPUT, result)
             self.assertEqual(f.read(), INPUT)
 
     def test_peek_bad_args(self):
diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py
index 95629ed..63998a8 100644
--- a/Lib/test/test_memoryio.py
+++ b/Lib/test/test_memoryio.py
@@ -265,8 +265,8 @@ def test_iterator(self):
         memio = self.ioclass(buf * 10)
 
         self.assertEqual(iter(memio), memio)
-        self.assertTrue(hasattr(memio, '__iter__'))
-        self.assertTrue(hasattr(memio, '__next__'))
+        self.assertHasAttr(memio, '__iter__')
+        self.assertHasAttr(memio, '__next__')
         i = 0
         for line in memio:
             self.assertEqual(line, buf)
diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py
index 9f131a9..4204a6a 100644
--- a/Lib/test/test_ordered_dict.py
+++ b/Lib/test/test_ordered_dict.py
@@ -147,7 +147,7 @@ def test_fromkeys(self):
     def test_abc(self):
         OrderedDict = self.OrderedDict
         self.assertIsInstance(OrderedDict(), MutableMapping)
-        self.assertTrue(issubclass(OrderedDict, MutableMapping))
+        self.assertIsSubclass(OrderedDict, MutableMapping)
 
     def test_clear(self):
         OrderedDict = self.OrderedDict
@@ -314,14 +314,14 @@ def check(dup):
         check(dup)
         self.assertIs(dup.x, od.x)
         self.assertIs(dup.z, od.z)
-        self.assertFalse(hasattr(dup, 'y'))
+        self.assertNotHasAttr(dup, 'y')
         dup = copy.deepcopy(od)
         check(dup)
         self.assertEqual(dup.x, od.x)
         self.assertIsNot(dup.x, od.x)
         self.assertEqual(dup.z, od.z)
         self.assertIsNot(dup.z, od.z)
-        self.assertFalse(hasattr(dup, 'y'))
+        self.assertNotHasAttr(dup, 'y')
         # pickle directly pulls the module, so we have to fake it
         with replaced_module('collections', self.module):
             for proto in range(pickle.HIGHEST_PROTOCOL + 1):
@@ -330,7 +330,7 @@ def check(dup):
                     check(dup)
                     self.assertEqual(dup.x, od.x)
                     self.assertEqual(dup.z, od.z)
-                    self.assertFalse(hasattr(dup, 'y'))
+                    self.assertNotHasAttr(dup, 'y')
         check(eval(repr(od)))
         update_test = OrderedDict()
         update_test.update(od)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 333179a..88b5b0e 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -818,7 +818,7 @@ def test_15261(self):
         self.assertEqual(ctx.exception.errno, errno.EBADF)
 
     def check_file_attributes(self, result):
-        self.assertTrue(hasattr(result, 'st_file_attributes'))
+        self.assertHasAttr(result, 'st_file_attributes')
         self.assertTrue(isinstance(result.st_file_attributes, int))
         self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF)
 
@@ -2181,7 +2181,7 @@ def test_getrandom0(self):
         self.assertEqual(empty, b'')
 
     def test_getrandom_random(self):
-        self.assertTrue(hasattr(os, 'GRND_RANDOM'))
+        self.assertHasAttr(os, 'GRND_RANDOM')
 
         # Don't test os.getrandom(1, os.GRND_RANDOM) to not consume the rare
         # resource /dev/random
@@ -5431,8 +5431,8 @@ def test_fsencode_fsdecode(self):
 
     def test_pathlike(self):
         self.assertEqual('#feelthegil', self.fspath(FakePath('#feelthegil')))
-        self.assertTrue(issubclass(FakePath, os.PathLike))
-        self.assertTrue(isinstance(FakePath('x'), os.PathLike))
+        self.assertIsSubclass(FakePath, os.PathLike)
+        self.assertIsInstance(FakePath('x'), os.PathLike)
 
     def test_garbage_in_exception_out(self):
         vapor = type('blah', (), {})
@@ -5458,8 +5458,8 @@ def test_pathlike_subclasshook(self):
         # true on abstract implementation.
         class A(os.PathLike):
             pass
-        self.assertFalse(issubclass(FakePath, A))
-        self.assertTrue(issubclass(FakePath, os.PathLike))
+        self.assertNotIsSubclass(FakePath, A)
+        self.assertIsSubclass(FakePath, os.PathLike)
 
     def test_pathlike_class_getitem(self):
         self.assertIsInstance(os.PathLike[bytes], types.GenericAlias)
@@ -5469,7 +5469,7 @@ class A(os.PathLike):
             __slots__ = ()
             def __fspath__(self):
                 return ''
-        self.assertFalse(hasattr(A(), '__dict__'))
+        self.assertNotHasAttr(A(), '__dict__')
 
     def test_fspath_set_to_None(self):
         class Foo:
diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py
index 37ef9fa..13356b4 100644
--- a/Lib/test/test_pathlib/test_pathlib.py
+++ b/Lib/test/test_pathlib/test_pathlib.py
@@ -77,8 +77,8 @@ def needs_symlinks(fn):
 
 class UnsupportedOperationTest(unittest.TestCase):
     def test_is_notimplemented(self):
-        self.assertTrue(issubclass(pathlib.UnsupportedOperation, NotImplementedError))
-        self.assertTrue(isinstance(pathlib.UnsupportedOperation(), NotImplementedError))
+        self.assertIsSubclass(pathlib.UnsupportedOperation, NotImplementedError)
+        self.assertIsInstance(pathlib.UnsupportedOperation(), NotImplementedError)
 
 
 class LazyImportTest(unittest.TestCase):
@@ -300,8 +300,8 @@ def test_repr_common(self):
                 clsname = p.__class__.__name__
                 r = repr(p)
                 # The repr() is in the form ClassName("forward-slashes path").
-                self.assertTrue(r.startswith(clsname + '('), r)
-                self.assertTrue(r.endswith(')'), r)
+                self.assertStartsWith(r, clsname + '(')
+                self.assertEndsWith(r, ')')
                 inner = r[len(clsname) + 1 : -1]
                 self.assertEqual(eval(inner), p.as_posix())
 
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index 47f51f1..0a9ba57 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -316,7 +316,7 @@ def negzero():
             return -(1.0-1.0)
 
         for instr in dis.get_instructions(negzero):
-            self.assertFalse(instr.opname.startswith('UNARY_'))
+            self.assertNotStartsWith(instr.opname, 'UNARY_')
         self.check_lnotab(negzero)
 
     def test_constant_folding_binop(self):
diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py
index 1095e73..aa01a9b 100644
--- a/Lib/test/test_peg_generator/test_c_parser.py
+++ b/Lib/test/test_peg_generator/test_c_parser.py
@@ -387,10 +387,10 @@ def test_with_stmt_with_paren(self) -> None:
         test_source = """
         stmt = "with (\\n    a as b,\\n    c as d\\n): pass"
         the_ast = parse.parse_string(stmt, mode=1)
-        self.assertTrue(ast_dump(the_ast).startswith(
+        self.assertStartsWith(ast_dump(the_ast),
             "Module(body=[With(items=[withitem(context_expr=Name(id='a', ctx=Load()), optional_vars=Name(id='b', ctx=Store())), "
             "withitem(context_expr=Name(id='c', ctx=Load()), optional_vars=Name(id='d', ctx=Store()))]"
-        ))
+        )
         """
         self.run_test(grammar_source, test_source)
 
diff --git a/Lib/test/test_peg_generator/test_pegen.py b/Lib/test/test_peg_generator/test_pegen.py
index d860652..d912c55 100644
--- a/Lib/test/test_peg_generator/test_pegen.py
+++ b/Lib/test/test_peg_generator/test_pegen.py
@@ -91,10 +91,8 @@ def test_gather(self) -> None:
         """
         rules = parse_string(grammar, GrammarParser).rules
         self.assertEqual(str(rules["start"]), "start: ','.thing+ NEWLINE")
-        self.assertTrue(
-            repr(rules["start"]).startswith(
-                "Rule('start', None, Rhs([Alt([NamedItem(None, Gather(StringLeaf(\"','\"), NameLeaf('thing'"
-            )
+        self.assertStartsWith(repr(rules["start"]),
+            "Rule('start', None, Rhs([Alt([NamedItem(None, Gather(StringLeaf(\"','\"), NameLeaf('thing'"
         )
         self.assertEqual(str(rules["thing"]), "thing: NUMBER")
         parser_class = make_parser(grammar)
diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py
index c176e50..21d097d 100644
--- a/Lib/test/test_perf_profiler.py
+++ b/Lib/test/test_perf_profiler.py
@@ -93,9 +93,7 @@ def baz():
                 perf_line, f"Could not find {expected_symbol} in perf file"
             )
             perf_addr = perf_line.split(" ")[0]
-            self.assertFalse(
-                perf_addr.startswith("0x"), "Address should not be prefixed with 0x"
-            )
+            self.assertNotStartsWith(perf_addr, "0x")
             self.assertTrue(
                 set(perf_addr).issubset(string.hexdigits),
                 "Address should contain only hex characters",
diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py
index 742ca8d..e2384b3 100644
--- a/Lib/test/test_pickle.py
+++ b/Lib/test/test_pickle.py
@@ -611,10 +611,10 @@ def test_name_mapping(self):
             with self.subTest(((module3, name3), (module2, name2))):
                 if (module2, name2) == ('exceptions', 'OSError'):
                     attr = getattribute(module3, name3)
-                    self.assertTrue(issubclass(attr, OSError))
+                    self.assertIsSubclass(attr, OSError)
                 elif (module2, name2) == ('exceptions', 'ImportError'):
                     attr = getattribute(module3, name3)
-                    self.assertTrue(issubclass(attr, ImportError))
+                    self.assertIsSubclass(attr, ImportError)
                 else:
                     module, name = mapping(module2, name2)
                     if module3[:1] != '_':
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 3b673a4..3688cc4 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -401,7 +401,7 @@ def test_win32_ver(self):
             for v in version.split('.'):
                 int(v)  # should not fail
         if csd:
-            self.assertTrue(csd.startswith('SP'), msg=csd)
+            self.assertStartsWith(csd, 'SP')
         if ptype:
             if os.cpu_count() > 1:
                 self.assertIn('Multiprocessor', ptype)
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index 0817d0a..628920e 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -1107,7 +1107,7 @@ def test_lchmod_dir_symlink(self):
 
     def _test_chflags_regular_file(self, chflags_func, target_file, **kwargs):
         st = os.stat(target_file)
-        self.assertTrue(hasattr(st, 'st_flags'))
+        self.assertHasAttr(st, 'st_flags')
 
         # ZFS returns EOPNOTSUPP when attempting to set flag UF_IMMUTABLE.
         flags = st.st_flags | stat.UF_IMMUTABLE
@@ -1143,7 +1143,7 @@ def test_lchflags_regular_file(self):
     def test_lchflags_symlink(self):
         testfn_st = os.stat(os_helper.TESTFN)
 
-        self.assertTrue(hasattr(testfn_st, 'st_flags'))
+        self.assertHasAttr(testfn_st, 'st_flags')
 
         self.addCleanup(os_helper.unlink, _DUMMY_SYMLINK)
         os.symlink(os_helper.TESTFN, _DUMMY_SYMLINK)
@@ -2218,12 +2218,12 @@ def _verify_available(self, name):
     def test_pwritev(self):
         self._verify_available("HAVE_PWRITEV")
         if self.mac_ver >= (10, 16):
-            self.assertTrue(hasattr(os, "pwritev"), "os.pwritev is not available")
-            self.assertTrue(hasattr(os, "preadv"), "os.readv is not available")
+            self.assertHasAttr(os, "pwritev")
+            self.assertHasAttr(os, "preadv")
 
         else:
-            self.assertFalse(hasattr(os, "pwritev"), "os.pwritev is available")
-            self.assertFalse(hasattr(os, "preadv"), "os.readv is available")
+            self.assertNotHasAttr(os, "pwritev")
+            self.assertNotHasAttr(os, "preadv")
 
     def test_stat(self):
         self._verify_available("HAVE_FSTATAT")
diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py
index cea241b..26aefdb 100644
--- a/Lib/test/test_property.py
+++ b/Lib/test/test_property.py
@@ -87,8 +87,8 @@ def test_property_decorator_baseclass(self):
         self.assertEqual(base.spam, 10)
         self.assertEqual(base._spam, 10)
         delattr(base, "spam")
-        self.assertTrue(not hasattr(base, "spam"))
-        self.assertTrue(not hasattr(base, "_spam"))
+        self.assertNotHasAttr(base, "spam")
+        self.assertNotHasAttr(base, "_spam")
         base.spam = 20
         self.assertEqual(base.spam, 20)
         self.assertEqual(base._spam, 20)
diff --git a/Lib/test/test_pulldom.py b/Lib/test/test_pulldom.py
index 6dc51e4..3c8ed25 100644
--- a/Lib/test/test_pulldom.py
+++ b/Lib/test/test_pulldom.py
@@ -46,7 +46,7 @@ def test_parse_semantics(self):
         items = pulldom.parseString(SMALL_SAMPLE)
         evt, node = next(items)
         # Just check the node is a Document:
-        self.assertTrue(hasattr(node, "createElement"))
+        self.assertHasAttr(node, "createElement")
         self.assertEqual(pulldom.START_DOCUMENT, evt)
         evt, node = next(items)
         self.assertEqual(pulldom.START_ELEMENT, evt)
@@ -192,7 +192,7 @@ def _test_thorough(self, pd, before_root=True):
         evt, node = next(pd)
         self.assertEqual(pulldom.START_DOCUMENT, evt)
         # Just check the node is a Document:
-        self.assertTrue(hasattr(node, "createElement"))
+        self.assertHasAttr(node, "createElement")
 
         if before_root:
             evt, node = next(pd)
diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py
index df05cd0..3e7b2cd 100644
--- a/Lib/test/test_pyclbr.py
+++ b/Lib/test/test_pyclbr.py
@@ -103,7 +103,7 @@ def ismethod(oclass, obj, name):
         for name, value in dict.items():
             if name in ignore:
                 continue
-            self.assertHasAttr(module, name, ignore)
+            self.assertHasAttr(module, name)
             py_item = getattr(module, name)
             if isinstance(value, pyclbr.Function):
                 self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py
index ac88b3c..281b24e 100644
--- a/Lib/test/test_pydoc/test_pydoc.py
+++ b/Lib/test/test_pydoc/test_pydoc.py
@@ -1380,7 +1380,7 @@ def test_modules_search_builtin(self):
             helper('modules garbage')
         result = help_io.getvalue()
 
-        self.assertTrue(result.startswith(expected))
+        self.assertStartsWith(result, expected)
 
     def test_importfile(self):
         try:
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index 43957f5..bd76d63 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -1415,27 +1415,27 @@ class CommandLineTest(unittest.TestCase):
     def test_parse_args(self):
         args, help_text = random._parse_args(shlex.split("--choice a b c"))
         self.assertEqual(args.choice, ["a", "b", "c"])
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
         args, help_text = random._parse_args(shlex.split("--integer 5"))
         self.assertEqual(args.integer, 5)
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
         args, help_text = random._parse_args(shlex.split("--float 2.5"))
         self.assertEqual(args.float, 2.5)
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
         args, help_text = random._parse_args(shlex.split("a b c"))
         self.assertEqual(args.input, ["a", "b", "c"])
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
         args, help_text = random._parse_args(shlex.split("5"))
         self.assertEqual(args.input, ["5"])
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
         args, help_text = random._parse_args(shlex.split("2.5"))
         self.assertEqual(args.input, ["2.5"])
-        self.assertTrue(help_text.startswith("usage: "))
+        self.assertStartsWith(help_text, "usage: ")
 
     def test_main(self):
         for command, expected in [
diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py
index f79a614..e9128ac 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -2868,11 +2868,11 @@ def test_long_pattern(self):
         pattern = 'Very %spattern' % ('long ' * 1000)
         r = repr(re.compile(pattern))
         self.assertLess(len(r), 300)
-        self.assertEqual(r[:30], "re.compile('Very long long lon")
+        self.assertStartsWith(r, "re.compile('Very long long lon")
         r = repr(re.compile(pattern, re.I))
         self.assertLess(len(r), 300)
-        self.assertEqual(r[:30], "re.compile('Very long long lon")
-        self.assertEqual(r[-16:], ", re.IGNORECASE)")
+        self.assertStartsWith(r, "re.compile('Very long long lon")
+        self.assertEndsWith(r, ", re.IGNORECASE)")
 
     def test_flags_repr(self):
         self.assertEqual(repr(re.I), "re.IGNORECASE")
@@ -2951,7 +2951,7 @@ def test_deprecated_modules(self):
                 self.assertEqual(mod.__name__, name)
                 self.assertEqual(mod.__package__, '')
                 for attr in deprecated[name]:
-                    self.assertTrue(hasattr(mod, attr))
+                    self.assertHasAttr(mod, attr)
                 del sys.modules[name]
 
     @cpython_only
diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py
index ffad350..1662365 100644
--- a/Lib/test/test_reprlib.py
+++ b/Lib/test/test_reprlib.py
@@ -173,13 +173,13 @@ def test_instance(self):
         eq(r(i3), ("<ClassWithFailingRepr instance at %#x>"%id(i3)))
 
         s = r(ClassWithFailingRepr)
-        self.assertTrue(s.startswith("<class "))
-        self.assertTrue(s.endswith(">"))
+        self.assertStartsWith(s, "<class ")
+        self.assertEndsWith(s, ">")
         self.assertIn(s.find("..."), [12, 13])
 
     def test_lambda(self):
         r = repr(lambda x: x)
-        self.assertTrue(r.startswith("<function ReprTests.test_lambda.<locals>.<lambda"), r)
+        self.assertStartsWith(r, "<function ReprTests.test_lambda.<locals>.<lambda")
         # XXX anonymous functions?  see func_repr
 
     def test_builtin_function(self):
@@ -187,8 +187,8 @@ def test_builtin_function(self):
         # Functions
         eq(repr(hash), '<built-in function hash>')
         # Methods
-        self.assertTrue(repr(''.split).startswith(
-            '<built-in method split of str object at 0x'))
+        self.assertStartsWith(repr(''.split),
+            '<built-in method split of str object at 0x')
 
     def test_range(self):
         eq = self.assertEqual
@@ -730,8 +730,8 @@ class baz:
         importlib.invalidate_caches()
         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import baz
         ibaz = baz.baz()
-        self.assertTrue(repr(ibaz).startswith(
-            "<%s.baz object at 0x" % baz.__name__))
+        self.assertStartsWith(repr(ibaz),
+            "<%s.baz object at 0x" % baz.__name__)
 
     def test_method(self):
         self._check_path_limitations('qux')
@@ -744,13 +744,13 @@ def amethod(self): pass
         from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux
         # Unbound methods first
         r = repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod)
-        self.assertTrue(r.startswith('<function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod'), r)
+        self.assertStartsWith(r, '<function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod')
         # Bound method next
         iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
         r = repr(iqux.amethod)
-        self.assertTrue(r.startswith(
+        self.assertStartsWith(r,
             '<bound method aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod of <%s.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa object at 0x' \
-            % (qux.__name__,) ), r)
+            % (qux.__name__,) )
 
     @unittest.skip('needs a built-in function with a really long name')
     def test_builtin_function(self):
diff --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py
index d403a0f..a891495 100644
--- a/Lib/test/test_rlcompleter.py
+++ b/Lib/test/test_rlcompleter.py
@@ -88,7 +88,7 @@ def create_expected_for_none():
                          ['CompleteMe._ham'])
         matches = self.completer.attr_matches('CompleteMe.__')
         for x in matches:
-            self.assertTrue(x.startswith('CompleteMe.__'), x)
+            self.assertStartsWith(x, 'CompleteMe.__')
         self.assertIn('CompleteMe.__name__', matches)
         self.assertIn('CompleteMe.__new__(', matches)
 
diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py
index ada78ec..a2a07c0 100644
--- a/Lib/test/test_runpy.py
+++ b/Lib/test/test_runpy.py
@@ -796,7 +796,7 @@ def assertSigInt(self, cmd, *args, **kwargs):
         # Use -E to ignore PYTHONSAFEPATH
         cmd = [sys.executable, '-E', *cmd]
         proc = subprocess.run(cmd, *args, **kwargs, text=True, stderr=subprocess.PIPE)
-        self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n"), proc.stderr)
+        self.assertEndsWith(proc.stderr, "\nKeyboardInterrupt\n")
         self.assertEqual(proc.returncode, self.EXPECTED_CODE)
 
     def test_pymain_run_file(self):
diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py
index 24a366e..520fbc1 100644
--- a/Lib/test/test_scope.py
+++ b/Lib/test/test_scope.py
@@ -778,7 +778,7 @@ class X:
         class X:
             locals()["x"] = 43
             del x
-        self.assertFalse(hasattr(X, "x"))
+        self.assertNotHasAttr(X, "x")
         self.assertEqual(x, 42)
 
     @cpython_only
diff --git a/Lib/test/test_script_helper.py b/Lib/test/test_script_helper.py
index f7871fd..eeea6c4 100644
--- a/Lib/test/test_script_helper.py
+++ b/Lib/test/test_script_helper.py
@@ -74,8 +74,7 @@ class TestScriptHelperEnvironment(unittest.TestCase):
     """Code coverage for interpreter_requires_environment()."""
 
     def setUp(self):
-        self.assertTrue(
-            hasattr(script_helper, '__cached_interp_requires_environment'))
+        self.assertHasAttr(script_helper, '__cached_interp_requires_environment')
         # Reset the private cached state.
         script_helper.__dict__['__cached_interp_requires_environment'] = None
 
diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py
index c01e323..c0df950 100644
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -237,7 +237,7 @@ def test_pickling(self):
             if type(self.s) not in (set, frozenset):
                 self.assertEqual(self.s.x, dup.x)
                 self.assertEqual(self.s.z, dup.z)
-                self.assertFalse(hasattr(self.s, 'y'))
+                self.assertNotHasAttr(self.s, 'y')
                 del self.s.x, self.s.z
 
     def test_iterator_pickling(self):
@@ -876,8 +876,8 @@ def test_repr(self):
 
     def check_repr_against_values(self):
         text = repr(self.set)
-        self.assertTrue(text.startswith('{'))
-        self.assertTrue(text.endswith('}'))
+        self.assertStartsWith(text, '{')
+        self.assertEndsWith(text, '}')
 
         result = text[1:-1].split(', ')
         result.sort()
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 87991fb..62c80aa 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -427,12 +427,12 @@ def check_args_to_onerror(self, func, arg, exc):
             else:
                 self.assertIs(func, os.listdir)
                 self.assertIn(arg, [TESTFN, self.child_dir_path])
-            self.assertTrue(issubclass(exc[0], OSError))
+            self.assertIsSubclass(exc[0], OSError)
             self.errorState += 1
         else:
             self.assertEqual(func, os.rmdir)
             self.assertEqual(arg, TESTFN)
-            self.assertTrue(issubclass(exc[0], OSError))
+            self.assertIsSubclass(exc[0], OSError)
             self.errorState = 3
 
     @unittest.skipIf(sys.platform[:6] == 'cygwin',
@@ -3479,7 +3479,7 @@ class PublicAPITests(unittest.TestCase):
     """Ensures that the correct values are exposed in the public API."""
 
     def test_module_all_attribute(self):
-        self.assertTrue(hasattr(shutil, '__all__'))
+        self.assertHasAttr(shutil, '__all__')
         target_api = ['copyfileobj', 'copyfile', 'copymode', 'copystat',
                       'copy', 'copy2', 'copytree', 'move', 'rmtree', 'Error',
                       'SpecialFileError', 'make_archive',
diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py
index a7e9241..d0e3294 100644
--- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -307,8 +307,7 @@ def test_getuserbase(self):
 
         with EnvironmentVarGuard() as environ:
             environ['PYTHONUSERBASE'] = 'xoxo'
-            self.assertTrue(site.getuserbase().startswith('xoxo'),
-                            site.getuserbase())
+            self.assertStartsWith(site.getuserbase(), 'xoxo')
 
     @unittest.skipUnless(HAS_USER_SITE, 'need user site')
     def test_getusersitepackages(self):
@@ -318,7 +317,7 @@ def test_getusersitepackages(self):
 
         # the call sets USER_BASE *and* USER_SITE
         self.assertEqual(site.USER_SITE, user_site)
-        self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
+        self.assertStartsWith(user_site, site.USER_BASE)
         self.assertEqual(site.USER_BASE, site.getuserbase())
 
     def test_getsitepackages(self):
@@ -359,11 +358,10 @@ def test_no_home_directory(self):
             environ.unset('PYTHONUSERBASE', 'APPDATA')
 
             user_base = site.getuserbase()
-            self.assertTrue(user_base.startswith('~' + os.sep),
-                            user_base)
+            self.assertStartsWith(user_base, '~' + os.sep)
 
             user_site = site.getusersitepackages()
-            self.assertTrue(user_site.startswith(user_base), user_site)
+            self.assertStartsWith(user_site, user_base)
 
         with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \
              mock.patch.object(site, 'addsitedir') as mock_addsitedir, \
@@ -495,18 +493,18 @@ def test_add_build_dir(self):
 
     def test_setting_quit(self):
         # 'quit' and 'exit' should be injected into builtins
-        self.assertTrue(hasattr(builtins, "quit"))
-        self.assertTrue(hasattr(builtins, "exit"))
+        self.assertHasAttr(builtins, "quit")
+        self.assertHasAttr(builtins, "exit")
 
     def test_setting_copyright(self):
         # 'copyright', 'credits', and 'license' should be in builtins
-        self.assertTrue(hasattr(builtins, "copyright"))
-        self.assertTrue(hasattr(builtins, "credits"))
-        self.assertTrue(hasattr(builtins, "license"))
+        self.assertHasAttr(builtins, "copyright")
+        self.assertHasAttr(builtins, "credits")
+        self.assertHasAttr(builtins, "license")
 
     def test_setting_help(self):
         # 'help' should be set in builtins
-        self.assertTrue(hasattr(builtins, "help"))
+        self.assertHasAttr(builtins, "help")
 
     def test_sitecustomize_executed(self):
         # If sitecustomize is available, it should have been imported.
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 03c5415..3dd67b2 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -1085,9 +1085,7 @@ def test3542SocketOptions(self):
             'IPV6_USE_MIN_MTU',
         }
         for opt in opts:
-            self.assertTrue(
-                hasattr(socket, opt), f"Missing RFC3542 socket option '{opt}'"
-            )
+            self.assertHasAttr(socket, opt)
 
     def testHostnameRes(self):
         # Testing hostname resolution mechanisms
@@ -1593,11 +1591,11 @@ def test_getsockaddrarg(self):
 
     @unittest.skipUnless(os.name == "nt", "Windows specific")
     def test_sock_ioctl(self):
-        self.assertTrue(hasattr(socket.socket, 'ioctl'))
-        self.assertTrue(hasattr(socket, 'SIO_RCVALL'))
-        self.assertTrue(hasattr(socket, 'RCVALL_ON'))
-        self.assertTrue(hasattr(socket, 'RCVALL_OFF'))
-        self.assertTrue(hasattr(socket, 'SIO_KEEPALIVE_VALS'))
+        self.assertHasAttr(socket.socket, 'ioctl')
+        self.assertHasAttr(socket, 'SIO_RCVALL')
+        self.assertHasAttr(socket, 'RCVALL_ON')
+        self.assertHasAttr(socket, 'RCVALL_OFF')
+        self.assertHasAttr(socket, 'SIO_KEEPALIVE_VALS')
         s = socket.socket()
         self.addCleanup(s.close)
         self.assertRaises(ValueError, s.ioctl, -1, None)
@@ -6082,10 +6080,10 @@ def testTimeoutZero(self):
 class TestExceptions(unittest.TestCase):
 
     def testExceptionTree(self):
-        self.assertTrue(issubclass(OSError, Exception))
-        self.assertTrue(issubclass(socket.herror, OSError))
-        self.assertTrue(issubclass(socket.gaierror, OSError))
-        self.assertTrue(issubclass(socket.timeout, OSError))
+        self.assertIsSubclass(OSError, Exception)
+        self.assertIsSubclass(socket.herror, OSError)
+        self.assertIsSubclass(socket.gaierror, OSError)
+        self.assertIsSubclass(socket.timeout, OSError)
         self.assertIs(socket.error, OSError)
         self.assertIs(socket.timeout, TimeoutError)
 
diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py
index 61b0077..1399f3f 100644
--- a/Lib/test/test_source_encoding.py
+++ b/Lib/test/test_source_encoding.py
@@ -145,8 +145,7 @@ def test_error_from_string(self):
             compile(input, "<string>", "exec")
         expected = "'ascii' codec can't decode byte 0xe2 in position 16: " \
                    "ordinal not in range(128)"
-        self.assertTrue(c.exception.args[0].startswith(expected),
-                        msg=c.exception.args[0])
+        self.assertStartsWith(c.exception.args[0], expected)
 
     def test_file_parse_error_multiline(self):
         # gh96611:
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 06460d6..2767a53 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -539,9 +539,9 @@ def test_openssl_version(self):
             openssl_ver = f"OpenSSL {major:d}.{minor:d}.{patch:d}"
         else:
             openssl_ver = f"OpenSSL {major:d}.{minor:d}.{fix:d}"
-        self.assertTrue(
-            s.startswith((openssl_ver, libressl_ver, "AWS-LC")),
-            (s, t, hex(n))
+        self.assertStartsWith(
+            s, (openssl_ver, libressl_ver, "AWS-LC"),
+            (t, hex(n))
         )
 
     @support.cpython_only
@@ -1668,7 +1668,7 @@ def test_lib_reason(self):
         regex = "(NO_START_LINE|UNSUPPORTED_PUBLIC_KEY_TYPE)"
         self.assertRegex(cm.exception.reason, regex)
         s = str(cm.exception)
-        self.assertTrue("NO_START_LINE" in s, s)
+        self.assertIn("NO_START_LINE", s)
 
     def test_subclass(self):
         # Check that the appropriate SSLError subclass is raised
@@ -1683,7 +1683,7 @@ def test_subclass(self):
                 with self.assertRaises(ssl.SSLWantReadError) as cm:
                     c.do_handshake()
                 s = str(cm.exception)
-                self.assertTrue(s.startswith("The operation did not complete (read)"), s)
+                self.assertStartsWith(s, "The operation did not complete (read)")
                 # For compatibility
                 self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ)
 
diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py
index 49013a4..5fd25d5 100644
--- a/Lib/test/test_stat.py
+++ b/Lib/test/test_stat.py
@@ -157,7 +157,7 @@ def test_mode(self):
 
             os.chmod(TESTFN, 0o700)
             st_mode, modestr = self.get_mode()
-            self.assertEqual(modestr[:3], '-rw')
+            self.assertStartsWith(modestr, '-rw')
             self.assertS_IS("REG", st_mode)
             self.assertEqual(self.statmod.S_IFMT(st_mode),
                              self.statmod.S_IFREG)
@@ -256,7 +256,7 @@ def test_flags_consistent(self):
                          "FILE_ATTRIBUTE_* constants are Win32 specific")
     def test_file_attribute_constants(self):
         for key, value in sorted(self.file_attributes.items()):
-            self.assertTrue(hasattr(self.statmod, key), key)
+            self.assertHasAttr(self.statmod, key)
             modvalue = getattr(self.statmod, key)
             self.assertEqual(value, modvalue, key)
 
@@ -314,7 +314,7 @@ def test_macosx_attribute_values(self):
         self.assertEqual(self.statmod.S_ISGID, 0o002000)
         self.assertEqual(self.statmod.S_ISVTX, 0o001000)
 
-        self.assertFalse(hasattr(self.statmod, "S_ISTXT"))
+        self.assertNotHasAttr(self.statmod, "S_ISTXT")
         self.assertEqual(self.statmod.S_IREAD, self.statmod.S_IRUSR)
         self.assertEqual(self.statmod.S_IWRITE, self.statmod.S_IWUSR)
         self.assertEqual(self.statmod.S_IEXEC, self.statmod.S_IXUSR)
diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py
index c69baa4..5980f93 100644
--- a/Lib/test/test_statistics.py
+++ b/Lib/test/test_statistics.py
@@ -645,7 +645,7 @@ def do_test(self, args):
 
     def test_numerictestcase_is_testcase(self):
         # Ensure that NumericTestCase actually is a TestCase.
-        self.assertTrue(issubclass(NumericTestCase, unittest.TestCase))
+        self.assertIsSubclass(NumericTestCase, unittest.TestCase)
 
     def test_error_msg_numeric(self):
         # Test the error message generated for numeric comparisons.
@@ -683,32 +683,23 @@ class GlobalsTest(unittest.TestCase):
     def test_meta(self):
         # Test for the existence of metadata.
         for meta in self.expected_metadata:
-            self.assertTrue(hasattr(self.module, meta),
-                            "%s not present" % meta)
+            self.assertHasAttr(self.module, meta)
 
     def test_check_all(self):
         # Check everything in __all__ exists and is public.
         module = self.module
         for name in module.__all__:
             # No private names in __all__:
-            self.assertFalse(name.startswith("_"),
+            self.assertNotStartsWith(name, "_",
                              'private name "%s" in __all__' % name)
             # And anything in __all__ must exist:
-            self.assertTrue(hasattr(module, name),
-                            'missing name "%s" in __all__' % name)
+            self.assertHasAttr(module, name)
 
 
 class StatisticsErrorTest(unittest.TestCase):
     def test_has_exception(self):
-        errmsg = (
-                "Expected StatisticsError to be a ValueError, but got a"
-                " subclass of %r instead."
-                )
-        self.assertTrue(hasattr(statistics, 'StatisticsError'))
-        self.assertTrue(
-                issubclass(statistics.StatisticsError, ValueError),
-                errmsg % statistics.StatisticsError.__base__
-                )
+        self.assertHasAttr(statistics, 'StatisticsError')
+        self.assertIsSubclass(statistics.StatisticsError, ValueError)
 
 
 # === Tests for private utility functions ===
diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py
index d0bc0bd..9622151 100644
--- a/Lib/test/test_structseq.py
+++ b/Lib/test/test_structseq.py
@@ -42,7 +42,7 @@ def test_repr(self):
         # os.stat() gives a complicated struct sequence.
         st = os.stat(__file__)
         rep = repr(st)
-        self.assertTrue(rep.startswith("os.stat_result"))
+        self.assertStartsWith(rep, "os.stat_result")
         self.assertIn("st_mode=", rep)
         self.assertIn("st_ino=", rep)
         self.assertIn("st_dev=", rep)
@@ -307,7 +307,7 @@ def test_copy_replace_with_invisible_fields(self):
         self.assertEqual(t5.tm_mon, 2)
 
         # named invisible fields
-        self.assertTrue(hasattr(t, 'tm_zone'), f"{t} has no attribute 'tm_zone'")
+        self.assertHasAttr(t, 'tm_zone')
         with self.assertRaisesRegex(AttributeError, 'readonly attribute'):
             t.tm_zone = 'some other zone'
         self.assertEqual(t2.tm_zone, t.tm_zone)
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index ca35804..f0e350c 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1178,7 +1178,7 @@ def test_universal_newlines_communicate_stdin_stdout_stderr(self):
         self.assertEqual("line1\nline2\nline3\nline4\nline5\n", stdout)
         # Python debug build push something like "[42442 refs]\n"
         # to stderr at exit of subprocess.
-        self.assertTrue(stderr.startswith("eline2\neline6\neline7\n"))
+        self.assertStartsWith(stderr, "eline2\neline6\neline7\n")
 
     def test_universal_newlines_communicate_encodings(self):
         # Check that universal newlines mode works for various encodings,
@@ -1510,7 +1510,7 @@ def test_issue8780(self):
                 "[sys.executable, '-c', 'print(\"Hello World!\")'])",
             'assert retcode == 0'))
         output = subprocess.check_output([sys.executable, '-c', code])
-        self.assertTrue(output.startswith(b'Hello World!'), ascii(output))
+        self.assertStartsWith(output, b'Hello World!')
 
     def test_handles_closed_on_exception(self):
         # If CreateProcess exits with an error, ensure the
@@ -1835,8 +1835,8 @@ def test_encoding_warning(self):
                             capture_output=True)
         lines = cp.stderr.splitlines()
         self.assertEqual(len(lines), 2, lines)
-        self.assertTrue(lines[0].startswith(b"<string>:2: EncodingWarning: "))
-        self.assertTrue(lines[1].startswith(b"<string>:3: EncodingWarning: "))
+        self.assertStartsWith(lines[0], b"<string>:2: EncodingWarning: ")
+        self.assertStartsWith(lines[1], b"<string>:3: EncodingWarning: ")
 
 
 def _get_test_grp_name():
diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py
index 5cef612..193c8b7 100644
--- a/Lib/test/test_super.py
+++ b/Lib/test/test_super.py
@@ -547,11 +547,11 @@ def test_special_methods(self):
             self.assertEqual(s.__reduce__, e.__reduce__)
             self.assertEqual(s.__reduce_ex__, e.__reduce_ex__)
             self.assertEqual(s.__getstate__, e.__getstate__)
-            self.assertFalse(hasattr(s, '__getnewargs__'))
-            self.assertFalse(hasattr(s, '__getnewargs_ex__'))
-            self.assertFalse(hasattr(s, '__setstate__'))
-            self.assertFalse(hasattr(s, '__copy__'))
-            self.assertFalse(hasattr(s, '__deepcopy__'))
+            self.assertNotHasAttr(s, '__getnewargs__')
+            self.assertNotHasAttr(s, '__getnewargs_ex__')
+            self.assertNotHasAttr(s, '__setstate__')
+            self.assertNotHasAttr(s, '__copy__')
+            self.assertNotHasAttr(s, '__deepcopy__')
 
     def test_pickling(self):
         e = E()
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index 8446da0..e48a246 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -407,10 +407,10 @@ class Obj:
         with support.swap_attr(obj, "y", 5) as y:
             self.assertEqual(obj.y, 5)
             self.assertIsNone(y)
-        self.assertFalse(hasattr(obj, 'y'))
+        self.assertNotHasAttr(obj, 'y')
         with support.swap_attr(obj, "y", 5):
             del obj.y
-        self.assertFalse(hasattr(obj, 'y'))
+        self.assertNotHasAttr(obj, 'y')
 
     def test_swap_item(self):
         D = {"x":1}
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index fb1c849..795d1ec 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -57,7 +57,7 @@ def test_original_displayhook(self):
             dh(None)
 
         self.assertEqual(out.getvalue(), "")
-        self.assertTrue(not hasattr(builtins, "_"))
+        self.assertNotHasAttr(builtins, "_")
 
         # sys.displayhook() requires arguments
         self.assertRaises(TypeError, dh)
@@ -172,7 +172,7 @@ def test_original_excepthook(self):
             with support.captured_stderr() as err:
                 sys.__excepthook__(*sys.exc_info())
 
-        self.assertTrue(err.getvalue().endswith("ValueError: 42\n"))
+        self.assertEndsWith(err.getvalue(), "ValueError: 42\n")
 
         self.assertRaises(TypeError, sys.__excepthook__)
 
@@ -192,7 +192,7 @@ def test_excepthook_bytes_filename(self):
         err = err.getvalue()
         self.assertIn("""  File "b'bytes_filename'", line 123\n""", err)
         self.assertIn("""    text\n""", err)
-        self.assertTrue(err.endswith("SyntaxError: msg\n"))
+        self.assertEndsWith(err, "SyntaxError: msg\n")
 
     def test_excepthook(self):
         with test.support.captured_output("stderr") as stderr:
@@ -269,8 +269,7 @@ def check_exit_message(code, expected, **env_vars):
             rc, out, err = assert_python_failure('-c', code, **env_vars)
             self.assertEqual(rc, 1)
             self.assertEqual(out, b'')
-            self.assertTrue(err.startswith(expected),
-                "%s doesn't start with %s" % (ascii(err), ascii(expected)))
+            self.assertStartsWith(err, expected)
 
         # test that stderr buffer is flushed before the exit message is written
         # into stderr
@@ -437,7 +436,7 @@ def test_call_tracing(self):
     @unittest.skipUnless(hasattr(sys, "setdlopenflags"),
                          'test needs sys.setdlopenflags()')
     def test_dlopenflags(self):
-        self.assertTrue(hasattr(sys, "getdlopenflags"))
+        self.assertHasAttr(sys, "getdlopenflags")
         self.assertRaises(TypeError, sys.getdlopenflags, 42)
         oldflags = sys.getdlopenflags()
         self.assertRaises(TypeError, sys.setdlopenflags)
@@ -623,8 +622,7 @@ def g456():
             # And the next record must be for g456().
             filename, lineno, funcname, sourceline = stack[i+1]
             self.assertEqual(funcname, "g456")
-            self.assertTrue((sourceline.startswith("if leave_g.wait(") or
-                             sourceline.startswith("g_raised.set()")))
+            self.assertStartsWith(sourceline, ("if leave_g.wait(", "g_raised.set()"))
         finally:
             # Reap the spawned thread.
             leave_g.set()
@@ -860,7 +858,7 @@ def test_sys_flags(self):
                  "hash_randomization", "isolated", "dev_mode", "utf8_mode",
                  "warn_default_encoding", "safe_path", "int_max_str_digits")
         for attr in attrs:
-            self.assertTrue(hasattr(sys.flags, attr), attr)
+            self.assertHasAttr(sys.flags, attr)
             attr_type = bool if attr in ("dev_mode", "safe_path") else int
             self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
         self.assertTrue(repr(sys.flags))
@@ -1072,10 +1070,10 @@ def test_implementation(self):
 
         levels = {'alpha': 0xA, 'beta': 0xB, 'candidate': 0xC, 'final': 0xF}
 
-        self.assertTrue(hasattr(sys.implementation, 'name'))
-        self.assertTrue(hasattr(sys.implementation, 'version'))
-        self.assertTrue(hasattr(sys.implementation, 'hexversion'))
-        self.assertTrue(hasattr(sys.implementation, 'cache_tag'))
+        self.assertHasAttr(sys.implementation, 'name')
+        self.assertHasAttr(sys.implementation, 'version')
+        self.assertHasAttr(sys.implementation, 'hexversion')
+        self.assertHasAttr(sys.implementation, 'cache_tag')
 
         version = sys.implementation.version
         self.assertEqual(version[:2], (version.major, version.minor))
@@ -1419,7 +1417,7 @@ def __del__(self):
                 else:
                     self.assertIn("ValueError", report)
                     self.assertIn("del is broken", report)
-                self.assertTrue(report.endswith("\n"))
+                self.assertEndsWith(report, "\n")
 
     def test_original_unraisablehook_exception_qualname(self):
         # See bpo-41031, bpo-45083.
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index 963cf75..d30f69d 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -186,7 +186,7 @@ def test_posix_venv_scheme(self):
         # The include directory on POSIX isn't exactly the same as before,
         # but it is "within"
         sysconfig_includedir = sysconfig.get_path('include', scheme='posix_venv', vars=vars)
-        self.assertTrue(sysconfig_includedir.startswith(incpath + os.sep))
+        self.assertStartsWith(sysconfig_includedir, incpath + os.sep)
 
     def test_nt_venv_scheme(self):
         # The following directories were hardcoded in the venv module
@@ -569,8 +569,7 @@ def test_linux_ext_suffix(self):
                 expected_suffixes = 'i386-linux-gnu.so', 'x86_64-linux-gnux32.so', 'i386-linux-musl.so'
             else: # 8 byte pointer size
                 expected_suffixes = 'x86_64-linux-gnu.so', 'x86_64-linux-musl.so'
-            self.assertTrue(suffix.endswith(expected_suffixes),
-                            f'unexpected suffix {suffix!r}')
+            self.assertEndsWith(suffix, expected_suffixes)
 
     @unittest.skipUnless(sys.platform == 'android', 'Android-specific test')
     def test_android_ext_suffix(self):
@@ -582,13 +581,12 @@ def test_android_ext_suffix(self):
             "aarch64": "aarch64-linux-android",
             "armv7l": "arm-linux-androideabi",
         }[machine]
-        self.assertTrue(suffix.endswith(f"-{expected_triplet}.so"),
-                        f"{machine=}, {suffix=}")
+        self.assertEndsWith(suffix, f"-{expected_triplet}.so")
 
     @unittest.skipUnless(sys.platform == 'darwin', 'OS X-specific test')
     def test_osx_ext_suffix(self):
         suffix = sysconfig.get_config_var('EXT_SUFFIX')
-        self.assertTrue(suffix.endswith('-darwin.so'), suffix)
+        self.assertEndsWith(suffix, '-darwin.so')
 
     def test_always_set_py_debug(self):
         self.assertIn('Py_DEBUG', sysconfig.get_config_vars())
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 2018a20..cf218a2 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -1650,7 +1650,7 @@ def test_cwd(self):
             try:
                 for t in tar:
                     if t.name != ".":
-                        self.assertTrue(t.name.startswith("./"), t.name)
+                        self.assertStartsWith(t.name, "./")
             finally:
                 tar.close()
 
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index d46d3c0..52b13b9 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -516,11 +516,11 @@ def test_collision_with_existing_file(self):
              _mock_candidate_names('aaa', 'aaa', 'bbb'):
             (fd1, name1) = self.make_temp()
             os.close(fd1)
-            self.assertTrue(name1.endswith('aaa'))
+            self.assertEndsWith(name1, 'aaa')
 
             (fd2, name2) = self.make_temp()
             os.close(fd2)
-            self.assertTrue(name2.endswith('bbb'))
+            self.assertEndsWith(name2, 'bbb')
 
     def test_collision_with_existing_directory(self):
         # _mkstemp_inner tries another name when a directory with
@@ -528,11 +528,11 @@ def test_collision_with_existing_directory(self):
         with _inside_empty_temp_dir(), \
              _mock_candidate_names('aaa', 'aaa', 'bbb'):
             dir = tempfile.mkdtemp()
-            self.assertTrue(dir.endswith('aaa'))
+            self.assertEndsWith(dir, 'aaa')
 
             (fd, name) = self.make_temp()
             os.close(fd)
-            self.assertTrue(name.endswith('bbb'))
+            self.assertEndsWith(name, 'bbb')
 
 
 class TestGetTempPrefix(BaseTestCase):
@@ -828,9 +828,9 @@ def test_collision_with_existing_file(self):
              _mock_candidate_names('aaa', 'aaa', 'bbb'):
             file = tempfile.NamedTemporaryFile(delete=False)
             file.close()
-            self.assertTrue(file.name.endswith('aaa'))
+            self.assertEndsWith(file.name, 'aaa')
             dir = tempfile.mkdtemp()
-            self.assertTrue(dir.endswith('bbb'))
+            self.assertEndsWith(dir, 'bbb')
 
     def test_collision_with_existing_directory(self):
         # mkdtemp tries another name when a directory with
@@ -838,9 +838,9 @@ def test_collision_with_existing_directory(self):
         with _inside_empty_temp_dir(), \
              _mock_candidate_names('aaa', 'aaa', 'bbb'):
             dir1 = tempfile.mkdtemp()
-            self.assertTrue(dir1.endswith('aaa'))
+            self.assertEndsWith(dir1, 'aaa')
             dir2 = tempfile.mkdtemp()
-            self.assertTrue(dir2.endswith('bbb'))
+            self.assertEndsWith(dir2, 'bbb')
 
     def test_for_tempdir_is_bytes_issue40701_api_warts(self):
         orig_tempdir = tempfile.tempdir
diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py
index e5d11cf..ce8392a 100644
--- a/Lib/test/test_termios.py
+++ b/Lib/test/test_termios.py
@@ -290,8 +290,8 @@ def test_ioctl_constants(self):
                 self.assertGreaterEqual(value, 0)
 
     def test_exception(self):
-        self.assertTrue(issubclass(termios.error, Exception))
-        self.assertFalse(issubclass(termios.error, OSError))
+        self.assertIsSubclass(termios.error, Exception)
+        self.assertNotIsSubclass(termios.error, OSError)
 
 
 if __name__ == '__main__':
diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py
index d06f652..5312faa 100644
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -761,17 +761,17 @@ def test_localtime_timezone(self):
 
         # Get the localtime and examine it for the offset and zone.
         lt = time.localtime()
-        self.assertTrue(hasattr(lt, "tm_gmtoff"))
-        self.assertTrue(hasattr(lt, "tm_zone"))
+        self.assertHasAttr(lt, "tm_gmtoff")
+        self.assertHasAttr(lt, "tm_zone")
 
         # See if the offset and zone are similar to the module
         # attributes.
         if lt.tm_gmtoff is None:
-            self.assertTrue(not hasattr(time, "timezone"))
+            self.assertNotHasAttr(time, "timezone")
         else:
             self.assertEqual(lt.tm_gmtoff, -[time.timezone, time.altzone][lt.tm_isdst])
         if lt.tm_zone is None:
-            self.assertTrue(not hasattr(time, "tzname"))
+            self.assertNotHasAttr(time, "tzname")
         else:
             self.assertEqual(lt.tm_zone, time.tzname[lt.tm_isdst])
 
@@ -1184,11 +1184,11 @@ def test_clock_functions(self):
 
         if mac_ver >= (10, 12):
             for name in clock_names:
-                self.assertTrue(hasattr(time, name), f"time.{name} is not available")
+                self.assertHasAttr(time, name)
 
         else:
             for name in clock_names:
-                self.assertFalse(hasattr(time, name), f"time.{name} is available")
+                self.assertNotHasAttr(time, name)
 
 
 if __name__ == "__main__":
diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py
index f5ae0a8..2aeebea 100644
--- a/Lib/test/test_timeit.py
+++ b/Lib/test/test_timeit.py
@@ -222,8 +222,8 @@ def test_repeat_function_zero_iters(self):
     def assert_exc_string(self, exc_string, expected_exc_name):
         exc_lines = exc_string.splitlines()
         self.assertGreater(len(exc_lines), 2)
-        self.assertTrue(exc_lines[0].startswith('Traceback'))
-        self.assertTrue(exc_lines[-1].startswith(expected_exc_name))
+        self.assertStartsWith(exc_lines[0], 'Traceback')
+        self.assertStartsWith(exc_lines[-1], expected_exc_name)
 
     def test_print_exc(self):
         s = io.StringIO()
diff --git a/Lib/test/test_tkinter/support.py b/Lib/test/test_tkinter/support.py
index ebb9e00..46b01e6 100644
--- a/Lib/test/test_tkinter/support.py
+++ b/Lib/test/test_tkinter/support.py
@@ -58,7 +58,7 @@ def _test_widget(self, constructor):
         destroy_default_root()
         tkinter.NoDefaultRoot()
         self.assertRaises(RuntimeError, constructor)
-        self.assertFalse(hasattr(tkinter, '_default_root'))
+        self.assertNotHasAttr(tkinter, '_default_root')
 
 
 def destroy_default_root():
diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py
index 96ea3f0..0c76e07 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -497,7 +497,7 @@ def test_info_patchlevel(self):
             self.assertEqual(vi.serial, 0)
         else:
             self.assertEqual(vi.micro, 0)
-        self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}'))
+        self.assertStartsWith(str(vi), f'{vi.major}.{vi.minor}')
 
     def test_embedded_null(self):
         widget = tkinter.Entry(self.root)
@@ -609,7 +609,7 @@ def test_focus(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, '??')
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, '??')
         self.assertEqual(e.state, '??')
         self.assertEqual(e.char, '??')
@@ -642,7 +642,7 @@ def test_configure(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, '??')
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, '??')
         self.assertEqual(e.state, '??')
         self.assertEqual(e.char, '??')
@@ -676,7 +676,7 @@ def test_event_generate_key_press(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, 0)
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, '??')
         self.assertIsInstance(e.state, int)
         self.assertNotEqual(e.state, 0)
@@ -747,7 +747,7 @@ def test_event_generate_button_press(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, 0)
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, 1)
         self.assertEqual(e.state, 0)
         self.assertEqual(e.char, '??')
@@ -781,7 +781,7 @@ def test_event_generate_motion(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, 0)
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, '??')
         self.assertEqual(e.state, 0x100)
         self.assertEqual(e.char, '??')
@@ -814,7 +814,7 @@ def test_event_generate_mouse_wheel(self):
         self.assertIs(e.widget, f)
         self.assertIsInstance(e.serial, int)
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.time, 0)
         self.assertEqual(e.num, '??')
         self.assertEqual(e.state, 0)
@@ -849,7 +849,7 @@ def test_generate_event_virtual_event(self):
         self.assertIsInstance(e.serial, int)
         self.assertEqual(e.time, 0)
         self.assertIs(e.send_event, False)
-        self.assertFalse(hasattr(e, 'focus'))
+        self.assertNotHasAttr(e, 'focus')
         self.assertEqual(e.num, '??')
         self.assertEqual(e.state, 0)
         self.assertEqual(e.char, '??')
@@ -1308,17 +1308,17 @@ def test_no_default_root(self):
         self.assertIs(tkinter._default_root, root)
         tkinter.NoDefaultRoot()
         self.assertIs(tkinter._support_default_root, False)
-        self.assertFalse(hasattr(tkinter, '_default_root'))
+        self.assertNotHasAttr(tkinter, '_default_root')
         # repeated call is no-op
         tkinter.NoDefaultRoot()
         self.assertIs(tkinter._support_default_root, False)
-        self.assertFalse(hasattr(tkinter, '_default_root'))
+        self.assertNotHasAttr(tkinter, '_default_root')
         root.destroy()
         self.assertIs(tkinter._support_default_root, False)
-        self.assertFalse(hasattr(tkinter, '_default_root'))
+        self.assertNotHasAttr(tkinter, '_default_root')
         root = tkinter.Tk()
         self.assertIs(tkinter._support_default_root, False)
-        self.assertFalse(hasattr(tkinter, '_default_root'))
+        self.assertNotHasAttr(tkinter, '_default_root')
         root.destroy()
 
     def test_getboolean(self):
diff --git a/Lib/test/test_type_comments.py b/Lib/test/test_type_comments.py
index ee8939f..c40c455 100644
--- a/Lib/test/test_type_comments.py
+++ b/Lib/test/test_type_comments.py
@@ -344,7 +344,7 @@ def test_longargs(self):
                 todo = set(t.name[1:])
                 self.assertEqual(len(t.args.args) + len(t.args.posonlyargs),
                                  len(todo) - bool(t.args.vararg) - bool(t.args.kwarg))
-                self.assertTrue(t.name.startswith('f'), t.name)
+                self.assertStartsWith(t.name, 'f')
                 for index, c in enumerate(t.name[1:]):
                     todo.remove(c)
                     if c == 'v':
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 3552b6b..3097c7d 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -827,15 +827,15 @@ def test_instancecheck_and_subclasscheck(self):
                 self.assertIsInstance(True, x)
                 self.assertIsInstance('a', x)
                 self.assertNotIsInstance(None, x)
-                self.assertTrue(issubclass(int, x))
-                self.assertTrue(issubclass(bool, x))
-                self.assertTrue(issubclass(str, x))
-                self.assertFalse(issubclass(type(None), x))
+                self.assertIsSubclass(int, x)
+                self.assertIsSubclass(bool, x)
+                self.assertIsSubclass(str, x)
+                self.assertNotIsSubclass(type(None), x)
 
         for x in (int | None, typing.Union[int, None]):
             with self.subTest(x=x):
                 self.assertIsInstance(None, x)
-                self.assertTrue(issubclass(type(None), x))
+                self.assertIsSubclass(type(None), x)
 
         for x in (
             int | collections.abc.Mapping,
@@ -844,8 +844,8 @@ def test_instancecheck_and_subclasscheck(self):
             with self.subTest(x=x):
                 self.assertIsInstance({}, x)
                 self.assertNotIsInstance((), x)
-                self.assertTrue(issubclass(dict, x))
-                self.assertFalse(issubclass(list, x))
+                self.assertIsSubclass(dict, x)
+                self.assertNotIsSubclass(list, x)
 
     def test_instancecheck_and_subclasscheck_order(self):
         T = typing.TypeVar('T')
@@ -857,7 +857,7 @@ def test_instancecheck_and_subclasscheck_order(self):
         for x in will_resolve:
             with self.subTest(x=x):
                 self.assertIsInstance(1, x)
-                self.assertTrue(issubclass(int, x))
+                self.assertIsSubclass(int, x)
 
         wont_resolve = (
             T | int,
@@ -890,7 +890,7 @@ class BadMeta(type):
             def __subclasscheck__(cls, sub):
                 1/0
         x = int | BadMeta('A', (), {})
-        self.assertTrue(issubclass(int, x))
+        self.assertIsSubclass(int, x)
         self.assertRaises(ZeroDivisionError, issubclass, list, x)
 
     def test_or_type_operator_with_TypeVar(self):
@@ -1399,7 +1399,7 @@ def test_new_class_basics(self):
 
     def test_new_class_subclass(self):
         C = types.new_class("C", (int,))
-        self.assertTrue(issubclass(C, int))
+        self.assertIsSubclass(C, int)
 
     def test_new_class_meta(self):
         Meta = self.Meta
@@ -1444,7 +1444,7 @@ def func(ns):
                             bases=(int,),
                             kwds=dict(metaclass=Meta, z=2),
                             exec_body=func)
-        self.assertTrue(issubclass(C, int))
+        self.assertIsSubclass(C, int)
         self.assertIsInstance(C, Meta)
         self.assertEqual(C.x, 0)
         self.assertEqual(C.y, 1)
diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py
index a04af55..d66cab1 100644
--- a/Lib/test/test_unittest/test_case.py
+++ b/Lib/test/test_unittest/test_case.py
@@ -1989,7 +1989,7 @@ def testAssertNoLogsYieldsNone(self):
             pass
         self.assertIsNone(value)
 
-    def testAssertStartswith(self):
+    def testAssertStartsWith(self):
         self.assertStartsWith('ababahalamaha', 'ababa')
         self.assertStartsWith('ababahalamaha', ('x', 'ababa', 'y'))
         self.assertStartsWith(UserString('ababahalamaha'), 'ababa')
@@ -2034,7 +2034,7 @@ def testAssertStartswith(self):
             self.assertStartsWith('ababahalamaha', 'amaha', msg='abracadabra')
         self.assertIn('ababahalamaha', str(cm.exception))
 
-    def testAssertNotStartswith(self):
+    def testAssertNotStartsWith(self):
         self.assertNotStartsWith('ababahalamaha', 'amaha')
         self.assertNotStartsWith('ababahalamaha', ('x', 'amaha', 'y'))
         self.assertNotStartsWith(UserString('ababahalamaha'), 'amaha')
@@ -2079,7 +2079,7 @@ def testAssertNotStartswith(self):
             self.assertNotStartsWith('ababahalamaha', 'ababa', msg='abracadabra')
         self.assertIn('ababahalamaha', str(cm.exception))
 
-    def testAssertEndswith(self):
+    def testAssertEndsWith(self):
         self.assertEndsWith('ababahalamaha', 'amaha')
         self.assertEndsWith('ababahalamaha', ('x', 'amaha', 'y'))
         self.assertEndsWith(UserString('ababahalamaha'), 'amaha')
@@ -2124,7 +2124,7 @@ def testAssertEndswith(self):
             self.assertEndsWith('ababahalamaha', 'ababa', msg='abracadabra')
         self.assertIn('ababahalamaha', str(cm.exception))
 
-    def testAssertNotEndswith(self):
+    def testAssertNotEndsWith(self):
         self.assertNotEndsWith('ababahalamaha', 'ababa')
         self.assertNotEndsWith('ababahalamaha', ('x', 'ababa', 'y'))
         self.assertNotEndsWith(UserString('ababahalamaha'), 'ababa')
diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py
index ace84ef..75de9ea 100644
--- a/Lib/test/test_userdict.py
+++ b/Lib/test/test_userdict.py
@@ -166,7 +166,7 @@ def test_update(self):
 
     def test_missing(self):
         # Make sure UserDict doesn't have a __missing__ method
-        self.assertEqual(hasattr(collections.UserDict, "__missing__"), False)
+        self.assertNotHasAttr(collections.UserDict, "__missing__")
         # Test several cases:
         # (D) subclass defines __missing__ method returning a value
         # (E) subclass defines __missing__ method raising RuntimeError
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index adc86a4..12c30e1 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -774,7 +774,7 @@ def test_activate_shell_script_has_no_dos_newlines(self):
         with open(script_path, 'rb') as script:
             for i, line in enumerate(script, 1):
                 error_message = f"CR LF found in line {i}"
-                self.assertFalse(line.endswith(b'\r\n'), error_message)
+                self.assertNotEndsWith(line, b'\r\n', error_message)
 
     @requireVenvCreate
     def test_scm_ignore_files_git(self):
@@ -978,7 +978,7 @@ def do_test_with_pip(self, system_site_packages):
         self.assertEqual(err, "")
         out = out.decode("latin-1") # Force to text, prevent decoding errors
         expected_version = "pip {}".format(ensurepip.version())
-        self.assertEqual(out[:len(expected_version)], expected_version)
+        self.assertStartsWith(out, expected_version)
         env_dir = os.fsencode(self.env_dir).decode("latin-1")
         self.assertIn(env_dir, out)
 
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index 05710c4..5c3b125 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -102,7 +102,7 @@ class PublicAPITests(BaseTest):
     """
 
     def test_module_all_attribute(self):
-        self.assertTrue(hasattr(self.module, '__all__'))
+        self.assertHasAttr(self.module, '__all__')
         target_api = ["warn", "warn_explicit", "showwarning",
                       "formatwarning", "filterwarnings", "simplefilter",
                       "resetwarnings", "catch_warnings", "deprecated"]
@@ -735,7 +735,7 @@ class CWarnTests(WarnTests, unittest.TestCase):
     # test.import_helper.import_fresh_module utility function
     def test_accelerated(self):
         self.assertIsNot(original_warnings, self.module)
-        self.assertFalse(hasattr(self.module.warn, '__code__'))
+        self.assertNotHasAttr(self.module.warn, '__code__')
 
 class PyWarnTests(WarnTests, unittest.TestCase):
     module = py_warnings
@@ -744,7 +744,7 @@ class PyWarnTests(WarnTests, unittest.TestCase):
     # test.import_helper.import_fresh_module utility function
     def test_pure_python(self):
         self.assertIsNot(original_warnings, self.module)
-        self.assertTrue(hasattr(self.module.warn, '__code__'))
+        self.assertHasAttr(self.module.warn, '__code__')
 
 
 class WCmdLineTests(BaseTest):
@@ -1528,12 +1528,12 @@ def test_late_resource_warning(self):
         # (_warnings will try to import it)
         code = "f = open(%a)" % __file__
         rc, out, err = assert_python_ok("-Wd", "-c", code)
-        self.assertTrue(err.startswith(expected), ascii(err))
+        self.assertStartsWith(err, expected)
 
         # import the warnings module
         code = "import warnings; f = open(%a)" % __file__
         rc, out, err = assert_python_ok("-Wd", "-c", code)
-        self.assertTrue(err.startswith(expected), ascii(err))
+        self.assertStartsWith(err, expected)
 
 
 class AsyncTests(BaseTest):
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 4faad66..4c7c900 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -432,7 +432,7 @@ def check_proxy(self, o, proxy):
         self.assertEqual(proxy.foo, 2,
                      "proxy does not reflect attribute modification")
         del o.foo
-        self.assertFalse(hasattr(proxy, 'foo'),
+        self.assertNotHasAttr(proxy, 'foo',
                      "proxy does not reflect attribute removal")
 
         proxy.foo = 1
@@ -442,7 +442,7 @@ def check_proxy(self, o, proxy):
         self.assertEqual(o.foo, 2,
             "object does not reflect attribute modification via proxy")
         del proxy.foo
-        self.assertFalse(hasattr(o, 'foo'),
+        self.assertNotHasAttr(o, 'foo',
                      "object does not reflect attribute removal via proxy")
 
     def test_proxy_deletion(self):
@@ -1108,7 +1108,7 @@ def meth(self):
         self.assertEqual(r.slot1, "abc")
         self.assertEqual(r.slot2, "def")
         self.assertEqual(r.meth(), "abcdef")
-        self.assertFalse(hasattr(r, "__dict__"))
+        self.assertNotHasAttr(r, "__dict__")
 
     def test_subclass_refs_with_cycle(self):
         """Confirm https://bugs.python.org/issue3100 is fixed."""
diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py
index 76e8e5c..c1e4f9c 100644
--- a/Lib/test/test_weakset.py
+++ b/Lib/test/test_weakset.py
@@ -466,7 +466,7 @@ def test_copying(self):
             self.assertIsNot(dup, s)
             self.assertIs(dup.x, s.x)
             self.assertIs(dup.z, s.z)
-            self.assertFalse(hasattr(dup, 'y'))
+            self.assertNotHasAttr(dup, 'y')
 
             dup = copy.deepcopy(s)
             self.assertIsInstance(dup, cls)
@@ -476,7 +476,7 @@ def test_copying(self):
             self.assertIsNot(dup.x, s.x)
             self.assertEqual(dup.z, s.z)
             self.assertIsNot(dup.z, s.z)
-            self.assertFalse(hasattr(dup, 'y'))
+            self.assertNotHasAttr(dup, 'y')
 
 
 if __name__ == "__main__":
diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py
index d9076e7..1bae884 100644
--- a/Lib/test/test_winconsoleio.py
+++ b/Lib/test/test_winconsoleio.py
@@ -17,9 +17,9 @@
 
 class WindowsConsoleIOTests(unittest.TestCase):
     def test_abc(self):
-        self.assertTrue(issubclass(ConIO, io.RawIOBase))
-        self.assertFalse(issubclass(ConIO, io.BufferedIOBase))
-        self.assertFalse(issubclass(ConIO, io.TextIOBase))
+        self.assertIsSubclass(ConIO, io.RawIOBase)
+        self.assertNotIsSubclass(ConIO, io.BufferedIOBase)
+        self.assertNotIsSubclass(ConIO, io.TextIOBase)
 
     def test_open_fd(self):
         self.assertRaisesRegex(ValueError,
diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py
index fd7abd1..f16611b 100644
--- a/Lib/test/test_with.py
+++ b/Lib/test/test_with.py
@@ -679,7 +679,7 @@ def testSingleComplexTarget(self):
         class C: pass
         blah = C()
         with mock_contextmanager_generator() as blah.foo:
-            self.assertEqual(hasattr(blah, "foo"), True)
+            self.assertHasAttr(blah, "foo")
 
     def testMultipleComplexTargets(self):
         class C:
diff --git a/Lib/test/test_wmi.py b/Lib/test/test_wmi.py
index ac7c9cb..90eb404 100644
--- a/Lib/test/test_wmi.py
+++ b/Lib/test/test_wmi.py
@@ -70,8 +70,8 @@ def test_wmi_query_overflow(self):
     def test_wmi_query_multiple_rows(self):
         # Multiple instances should have an extra null separator
         r = wmi_exec_query("SELECT ProcessId FROM Win32_Process WHERE ProcessId < 1000")
-        self.assertFalse(r.startswith("\0"), r)
-        self.assertFalse(r.endswith("\0"), r)
+        self.assertNotStartsWith(r, "\0")
+        self.assertNotEndsWith(r, "\0")
         it = iter(r.split("\0"))
         try:
             while True:
diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py
index b047f7b..e04a4d2 100644
--- a/Lib/test/test_wsgiref.py
+++ b/Lib/test/test_wsgiref.py
@@ -149,9 +149,9 @@ def bad_app(environ,start_response):
             start_response("200 OK", ('Content-Type','text/plain'))
             return ["Hello, world!"]
         out, err = run_amock(validator(bad_app))
-        self.assertTrue(out.endswith(
+        self.assertEndsWith(out,
             b"A server error occurred.  Please contact the administrator."
-        ))
+        )
         self.assertEqual(
             err.splitlines()[-2],
             "AssertionError: Headers (('Content-Type', 'text/plain')) must"
@@ -174,9 +174,9 @@ def bad_app(environ, start_response):
         for status, exc_message in tests:
             with self.subTest(status=status):
                 out, err = run_amock(create_bad_app(status))
-                self.assertTrue(out.endswith(
+                self.assertEndsWith(out,
                     b"A server error occurred.  Please contact the administrator."
-                ))
+                )
                 self.assertEqual(err.splitlines()[-2], exc_message)
 
     def test_wsgi_input(self):
@@ -185,9 +185,9 @@ def bad_app(e,s):
             s("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
             return [b"data"]
         out, err = run_amock(validator(bad_app))
-        self.assertTrue(out.endswith(
+        self.assertEndsWith(out,
             b"A server error occurred.  Please contact the administrator."
-        ))
+        )
         self.assertEqual(
             err.splitlines()[-2], "AssertionError"
         )
@@ -200,7 +200,7 @@ def app(e, s):
                 ])
             return [b"data"]
         out, err = run_amock(validator(app))
-        self.assertTrue(err.endswith('"GET / HTTP/1.0" 200 4\n'))
+        self.assertEndsWith(err, '"GET / HTTP/1.0" 200 4\n')
         ver = sys.version.split()[0].encode('ascii')
         py  = python_implementation().encode('ascii')
         pyver = py + b"/" + ver
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 8f27795..38be2cd 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -225,8 +225,7 @@ def check_element(element):
             self.assertTrue(ET.iselement(element), msg="not an element")
             direlem = dir(element)
             for attr in 'tag', 'attrib', 'text', 'tail':
-                self.assertTrue(hasattr(element, attr),
-                        msg='no %s member' % attr)
+                self.assertHasAttr(element, attr)
                 self.assertIn(attr, direlem,
                         msg='no %s visible by dir' % attr)
 
@@ -251,7 +250,7 @@ def check_element(element):
         # Make sure all standard element methods exist.
 
         def check_method(method):
-            self.assertTrue(hasattr(method, '__call__'),
+            self.assertHasAttr(method, '__call__',
                     msg="%s not callable" % method)
 
         check_method(element.append)
diff --git a/Lib/test/test_xxlimited.py b/Lib/test/test_xxlimited.py
index 6dbfb3f..b52e78b 100644
--- a/Lib/test/test_xxlimited.py
+++ b/Lib/test/test_xxlimited.py
@@ -31,7 +31,7 @@ def test_foo(self):
         self.assertEqual(self.module.foo(1, 2), 3)
 
     def test_str(self):
-        self.assertTrue(issubclass(self.module.Str, str))
+        self.assertIsSubclass(self.module.Str, str)
         self.assertIsNot(self.module.Str, str)
 
         custom_string = self.module.Str("abcd")
diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py
index d4766c5..8fb0a68 100644
--- a/Lib/test/test_zipapp.py
+++ b/Lib/test/test_zipapp.py
@@ -259,7 +259,7 @@ def test_pack_to_fileobj(self):
         (source / '__main__.py').touch()
         target = io.BytesIO()
         zipapp.create_archive(str(source), target, interpreter='python')
-        self.assertTrue(target.getvalue().startswith(b'#!python\n'))
+        self.assertStartsWith(target.getvalue(), b'#!python\n')
 
     def test_read_shebang(self):
         # Test that we can read the shebang line correctly.
@@ -300,7 +300,7 @@ def test_write_shebang_to_fileobj(self):
         zipapp.create_archive(str(source), str(target), interpreter='python')
         new_target = io.BytesIO()
         zipapp.create_archive(str(target), new_target, interpreter='python2.7')
-        self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n'))
+        self.assertStartsWith(new_target.getvalue(), b'#!python2.7\n')
 
     def test_read_from_pathlike_obj(self):
         # Test that we can copy an archive using a path-like object
@@ -326,7 +326,7 @@ def test_read_from_fileobj(self):
         new_target = io.BytesIO()
         temp_archive.seek(0)
         zipapp.create_archive(temp_archive, new_target, interpreter='python2.7')
-        self.assertTrue(new_target.getvalue().startswith(b'#!python2.7\n'))
+        self.assertStartsWith(new_target.getvalue(), b'#!python2.7\n')
 
     def test_remove_shebang(self):
         # Test that we can remove the shebang from a file.
diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py
index e936039..ada9681 100644
--- a/Lib/test/test_zipfile/test_core.py
+++ b/Lib/test/test_zipfile/test_core.py
@@ -3198,7 +3198,7 @@ def test_write_dir(self):
         with zipfile.ZipFile(TESTFN, "w") as zipf:
             zipf.write(dirpath)
             zinfo = zipf.filelist[0]
-            self.assertTrue(zinfo.filename.endswith("/x/"))
+            self.assertEndsWith(zinfo.filename, "/x/")
             self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
             zipf.write(dirpath, "y")
             zinfo = zipf.filelist[1]
@@ -3206,7 +3206,7 @@ def test_write_dir(self):
             self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
         with zipfile.ZipFile(TESTFN, "r") as zipf:
             zinfo = zipf.filelist[0]
-            self.assertTrue(zinfo.filename.endswith("/x/"))
+            self.assertEndsWith(zinfo.filename, "/x/")
             self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
             zinfo = zipf.filelist[1]
             self.assertTrue(zinfo.filename, "y/")
@@ -3226,7 +3226,7 @@ def test_writestr_dir(self):
             self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
         with zipfile.ZipFile(TESTFN, "r") as zipf:
             zinfo = zipf.filelist[0]
-            self.assertTrue(zinfo.filename.endswith("x/"))
+            self.assertEndsWith(zinfo.filename, "x/")
             self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
             target = os.path.join(TESTFN2, "target")
             os.mkdir(target)
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index 1f288c8..b5b4acf 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -835,11 +835,11 @@ def doTraceback(self, module):
 
             s = io.StringIO()
             print_tb(tb, 1, s)
-            self.assertTrue(s.getvalue().endswith(
+            self.assertEndsWith(s.getvalue(),
                 '    def do_raise(): raise TypeError\n'
                 '' if support.has_no_debug_ranges() else
                 '                    ^^^^^^^^^^^^^^^\n'
-            ))
+            )
         else:
             raise AssertionError("This ought to be impossible")
 
diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py
index d284549..f313e39 100644
--- a/Lib/test/test_zoneinfo/test_zoneinfo.py
+++ b/Lib/test/test_zoneinfo/test_zoneinfo.py
@@ -1915,8 +1915,8 @@ class ExtensionBuiltTest(unittest.TestCase):
     def test_cache_location(self):
         # The pure Python version stores caches on attributes, but the C
         # extension stores them in C globals (at least for now)
-        self.assertFalse(hasattr(c_zoneinfo.ZoneInfo, "_weak_cache"))
-        self.assertTrue(hasattr(py_zoneinfo.ZoneInfo, "_weak_cache"))
+        self.assertNotHasAttr(c_zoneinfo.ZoneInfo, "_weak_cache")
+        self.assertHasAttr(py_zoneinfo.ZoneInfo, "_weak_cache")
 
     def test_gc_tracked(self):
         import gc