diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py
index 1663b1f1143ddc..99c94ff14ca36d 100644
--- a/Lib/test/test_minidom.py
+++ b/Lib/test/test_minidom.py
@@ -1,16 +1,20 @@
-# test for xml.dom.minidom
+"""Tests for xml.dom.minidom."""
 
 import copy
-import pickle
 import io
+import pickle
 from test import support
 import unittest
 
+from xml.dom.minicompat import NodeList
 import xml.dom.minidom
-
-from xml.dom.minidom import parse, Node, Document, parseString
+from xml.dom.minidom import Document
+from xml.dom.minidom import DocumentFragment
+from xml.dom.minidom import Element
 from xml.dom.minidom import getDOMImplementation
-
+from xml.dom.minidom import Node
+from xml.dom.minidom import parse
+from xml.dom.minidom import parseString
 
 tstfile = support.findfile("test.xml", subdir="xmltestdata")
 sample = ("\n"
@@ -81,49 +85,6 @@ def testGetElementsByTagName(self):
                 dom.documentElement.getElementsByTagName("LI"))
         dom.unlink()
 
-    def testInsertBefore(self):
-        dom = parseString("")
-        root = dom.documentElement
-        elem = root.childNodes[0]
-        nelem = dom.createElement("element")
-        root.insertBefore(nelem, elem)
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2
-                and root.childNodes[0] is nelem
-                and root.childNodes.item(0) is nelem
-                and root.childNodes[1] is elem
-                and root.childNodes.item(1) is elem
-                and root.firstChild is nelem
-                and root.lastChild is elem
-                and root.toxml() == ""
-                , "testInsertBefore -- node properly placed in tree")
-        nelem = dom.createElement("element")
-        root.insertBefore(nelem, None)
-        self.confirm(len(root.childNodes) == 3
-                and root.childNodes.length == 3
-                and root.childNodes[1] is elem
-                and root.childNodes.item(1) is elem
-                and root.childNodes[2] is nelem
-                and root.childNodes.item(2) is nelem
-                and root.lastChild is nelem
-                and nelem.previousSibling is elem
-                and root.toxml() == ""
-                , "testInsertBefore -- node properly placed in tree")
-        nelem2 = dom.createElement("bar")
-        root.insertBefore(nelem2, nelem)
-        self.confirm(len(root.childNodes) == 4
-                and root.childNodes.length == 4
-                and root.childNodes[2] is nelem2
-                and root.childNodes.item(2) is nelem2
-                and root.childNodes[3] is nelem
-                and root.childNodes.item(3) is nelem
-                and nelem2.nextSibling is nelem
-                and nelem.previousSibling is nelem2
-                and root.toxml() ==
-                ""
-                , "testInsertBefore -- node properly placed in tree")
-        dom.unlink()
-
     def _create_fragment_test_nodes(self):
         dom = parseString("")
         orig = dom.createTextNode("original")
@@ -137,48 +98,6 @@ def _create_fragment_test_nodes(self):
         frag.appendChild(c3)
         return dom, orig, c1, c2, c3, frag
 
-    def testInsertBeforeFragment(self):
-        dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
-        dom.documentElement.insertBefore(frag, None)
-        self.confirm(tuple(dom.documentElement.childNodes) ==
-                     (orig, c1, c2, c3),
-                     "insertBefore(, None)")
-        frag.unlink()
-        dom.unlink()
-
-        dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
-        dom.documentElement.insertBefore(frag, orig)
-        self.confirm(tuple(dom.documentElement.childNodes) ==
-                     (c1, c2, c3, orig),
-                     "insertBefore(, orig)")
-        frag.unlink()
-        dom.unlink()
-
-    def testAppendChild(self):
-        dom = parse(tstfile)
-        dom.documentElement.appendChild(dom.createComment("Hello"))
-        self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
-        self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
-        dom.unlink()
-
-    def testAppendChildFragment(self):
-        dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
-        dom.documentElement.appendChild(frag)
-        self.confirm(tuple(dom.documentElement.childNodes) ==
-                     (orig, c1, c2, c3),
-                     "appendChild()")
-        frag.unlink()
-        dom.unlink()
-
-    def testReplaceChildFragment(self):
-        dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
-        dom.documentElement.replaceChild(frag, orig)
-        orig.unlink()
-        self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
-                "replaceChild()")
-        frag.unlink()
-        dom.unlink()
-
     def testLegalChildren(self):
         dom = Document()
         elem = dom.createElement('element')
@@ -380,15 +299,6 @@ def testChangeAttr(self):
                 and el.getAttribute("spam2") == "bam2")
         dom.unlink()
 
-    def testGetAttrList(self):
-        pass
-
-    def testGetAttrValues(self):
-        pass
-
-    def testGetAttrLength(self):
-        pass
-
     def testGetAttribute(self):
         dom = Document()
         child = dom.appendChild(
@@ -409,8 +319,6 @@ def testGetAttributeNS(self):
         self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"),
                          '')
 
-    def testGetAttributeNode(self): pass
-
     def testGetElementsByTagNameNS(self):
         d="""
         
@@ -481,8 +389,6 @@ def testAttributeRepr(self):
         self.confirm(str(node) == repr(node))
         dom.unlink()
 
-    def testTextNodeRepr(self): pass
-
     def testWriteXML(self):
         str = ''
         dom = parseString(str)
@@ -546,14 +452,6 @@ def testProcessingInstruction(self):
                 and pi.localName is None
                 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
 
-    def testProcessingInstructionRepr(self): pass
-
-    def testTextRepr(self): pass
-
-    def testWriteText(self): pass
-
-    def testDocumentElement(self): pass
-
     def testTooManyDocumentElements(self):
         doc = parseString("")
         elem = doc.createElement("extra")
@@ -562,26 +460,6 @@ def testTooManyDocumentElements(self):
         elem.unlink()
         doc.unlink()
 
-    def testCreateElementNS(self): pass
-
-    def testCreateAttributeNS(self): pass
-
-    def testParse(self): pass
-
-    def testParseString(self): pass
-
-    def testComment(self): pass
-
-    def testAttrListItem(self): pass
-
-    def testAttrListItems(self): pass
-
-    def testAttrListItemNS(self): pass
-
-    def testAttrListKeys(self): pass
-
-    def testAttrListKeysNS(self): pass
-
     def testRemoveNamedItem(self):
         doc = parseString("")
         e = doc.documentElement
@@ -601,88 +479,6 @@ def testRemoveNamedItemNS(self):
         self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
                           "http://xml.python.org/", "b")
 
-    def testAttrListValues(self): pass
-
-    def testAttrListLength(self): pass
-
-    def testAttrList__getitem__(self): pass
-
-    def testAttrList__setitem__(self): pass
-
-    def testSetAttrValueandNodeValue(self): pass
-
-    def testParseElement(self): pass
-
-    def testParseAttributes(self): pass
-
-    def testParseElementNamespaces(self): pass
-
-    def testParseAttributeNamespaces(self): pass
-
-    def testParseProcessingInstructions(self): pass
-
-    def testChildNodes(self): pass
-
-    def testFirstChild(self): pass
-
-    def testHasChildNodes(self):
-        dom = parseString("")
-        doc = dom.documentElement
-        self.assertTrue(doc.hasChildNodes())
-        dom2 = parseString("")
-        doc2 = dom2.documentElement
-        self.assertFalse(doc2.hasChildNodes())
-
-    def _testCloneElementCopiesAttributes(self, e1, e2, test):
-        attrs1 = e1.attributes
-        attrs2 = e2.attributes
-        keys1 = list(attrs1.keys())
-        keys2 = list(attrs2.keys())
-        keys1.sort()
-        keys2.sort()
-        self.confirm(keys1 == keys2, "clone of element has same attribute keys")
-        for i in range(len(keys1)):
-            a1 = attrs1.item(i)
-            a2 = attrs2.item(i)
-            self.confirm(a1 is not a2
-                    and a1.value == a2.value
-                    and a1.nodeValue == a2.nodeValue
-                    and a1.namespaceURI == a2.namespaceURI
-                    and a1.localName == a2.localName
-                    , "clone of attribute node has proper attribute values")
-            self.confirm(a2.ownerElement is e2,
-                    "clone of attribute node correctly owned")
-
-    def _setupCloneElement(self, deep):
-        dom = parseString("")
-        root = dom.documentElement
-        clone = root.cloneNode(deep)
-        self._testCloneElementCopiesAttributes(
-            root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
-        # mutilate the original so shared data is detected
-        root.tagName = root.nodeName = "MODIFIED"
-        root.setAttribute("attr", "NEW VALUE")
-        root.setAttribute("added", "VALUE")
-        return dom, clone
-
-    def testCloneElementShallow(self):
-        dom, clone = self._setupCloneElement(0)
-        self.confirm(len(clone.childNodes) == 0
-                and clone.childNodes.length == 0
-                and clone.parentNode is None
-                and clone.toxml() == ''
-                , "testCloneElementShallow")
-        dom.unlink()
-
-    def testCloneElementDeep(self):
-        dom, clone = self._setupCloneElement(1)
-        self.confirm(len(clone.childNodes) == 1
-                and clone.childNodes.length == 1
-                and clone.parentNode is None
-                and clone.toxml() == ''
-                , "testCloneElementDeep")
-        dom.unlink()
-
     def testCloneDocumentShallow(self):
         doc = parseString("\n"
                     ""
@@ -888,208 +684,6 @@ def testCloneNodeEntity(self):
         self.check_clone_node_entity(False)
         self.check_clone_node_entity(True)
 
-    def testNormalize(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode("first"))
-        root.appendChild(doc.createTextNode("second"))
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2,
-                "testNormalize -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 1
-                and root.childNodes.length == 1
-                and root.firstChild is root.lastChild
-                and root.firstChild.data == "firstsecond"
-                , "testNormalize -- result")
-        doc.unlink()
-
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode(""))
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 0
-                and root.childNodes.length == 0,
-                "testNormalize -- single empty node removed")
-        doc.unlink()
-
-    def testNormalizeCombineAndNextSibling(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode("first"))
-        root.appendChild(doc.createTextNode("second"))
-        root.appendChild(doc.createElement("i"))
-        self.confirm(len(root.childNodes) == 3
-                and root.childNodes.length == 3,
-                "testNormalizeCombineAndNextSibling -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2
-                and root.firstChild.data == "firstsecond"
-                and root.firstChild is not root.lastChild
-                and root.firstChild.nextSibling is root.lastChild
-                and root.firstChild.previousSibling is None
-                and root.lastChild.previousSibling is root.firstChild
-                and root.lastChild.nextSibling is None
-                , "testNormalizeCombinedAndNextSibling -- result")
-        doc.unlink()
-
-    def testNormalizeDeleteWithPrevSibling(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode("first"))
-        root.appendChild(doc.createTextNode(""))
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2,
-                "testNormalizeDeleteWithPrevSibling -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 1
-                and root.childNodes.length == 1
-                and root.firstChild.data == "first"
-                and root.firstChild is root.lastChild
-                and root.firstChild.nextSibling is None
-                and root.firstChild.previousSibling is None
-                , "testNormalizeDeleteWithPrevSibling -- result")
-        doc.unlink()
-
-    def testNormalizeDeleteWithNextSibling(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode(""))
-        root.appendChild(doc.createTextNode("second"))
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2,
-                "testNormalizeDeleteWithNextSibling -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 1
-                and root.childNodes.length == 1
-                and root.firstChild.data == "second"
-                and root.firstChild is root.lastChild
-                and root.firstChild.nextSibling is None
-                and root.firstChild.previousSibling is None
-                , "testNormalizeDeleteWithNextSibling -- result")
-        doc.unlink()
-
-    def testNormalizeDeleteWithTwoNonTextSiblings(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createElement("i"))
-        root.appendChild(doc.createTextNode(""))
-        root.appendChild(doc.createElement("i"))
-        self.confirm(len(root.childNodes) == 3
-                and root.childNodes.length == 3,
-                "testNormalizeDeleteWithTwoSiblings -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2
-                and root.firstChild is not root.lastChild
-                and root.firstChild.nextSibling is root.lastChild
-                and root.firstChild.previousSibling is None
-                and root.lastChild.previousSibling is root.firstChild
-                and root.lastChild.nextSibling is None
-                , "testNormalizeDeleteWithTwoSiblings -- result")
-        doc.unlink()
-
-    def testNormalizeDeleteAndCombine(self):
-        doc = parseString("")
-        root = doc.documentElement
-        root.appendChild(doc.createTextNode(""))
-        root.appendChild(doc.createTextNode("second"))
-        root.appendChild(doc.createTextNode(""))
-        root.appendChild(doc.createTextNode("fourth"))
-        root.appendChild(doc.createTextNode(""))
-        self.confirm(len(root.childNodes) == 5
-                and root.childNodes.length == 5,
-                "testNormalizeDeleteAndCombine -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 1
-                and root.childNodes.length == 1
-                and root.firstChild is root.lastChild
-                and root.firstChild.data == "secondfourth"
-                and root.firstChild.previousSibling is None
-                and root.firstChild.nextSibling is None
-                , "testNormalizeDeleteAndCombine -- result")
-        doc.unlink()
-
-    def testNormalizeRecursion(self):
-        doc = parseString(""
-                            ""
-                              ""
-                              "t"
-                              #
-                              #x
-                            ""
-                            ""
-                              ""
-                                "t2"
-                                #x2
-                              ""
-                              "t3"
-                              #x3
-                            ""
-                            #
-                          "")
-        root = doc.documentElement
-        root.childNodes[0].appendChild(doc.createTextNode(""))
-        root.childNodes[0].appendChild(doc.createTextNode("x"))
-        root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
-        root.childNodes[1].appendChild(doc.createTextNode("x3"))
-        root.appendChild(doc.createTextNode(""))
-        self.confirm(len(root.childNodes) == 3
-                and root.childNodes.length == 3
-                and len(root.childNodes[0].childNodes) == 4
-                and root.childNodes[0].childNodes.length == 4
-                and len(root.childNodes[1].childNodes) == 3
-                and root.childNodes[1].childNodes.length == 3
-                and len(root.childNodes[1].childNodes[0].childNodes) == 2
-                and root.childNodes[1].childNodes[0].childNodes.length == 2
-                , "testNormalize2 -- preparation")
-        doc.normalize()
-        self.confirm(len(root.childNodes) == 2
-                and root.childNodes.length == 2
-                and len(root.childNodes[0].childNodes) == 2
-                and root.childNodes[0].childNodes.length == 2
-                and len(root.childNodes[1].childNodes) == 2
-                and root.childNodes[1].childNodes.length == 2
-                and len(root.childNodes[1].childNodes[0].childNodes) == 1
-                and root.childNodes[1].childNodes[0].childNodes.length == 1
-                , "testNormalize2 -- childNodes lengths")
-        self.confirm(root.childNodes[0].childNodes[1].data == "tx"
-                and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
-                and root.childNodes[1].childNodes[1].data == "t3x3"
-                , "testNormalize2 -- joined text fields")
-        self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
-                and root.childNodes[0].childNodes[1].previousSibling
-                        is root.childNodes[0].childNodes[0]
-                and root.childNodes[0].childNodes[0].previousSibling is None
-                and root.childNodes[0].childNodes[0].nextSibling
-                        is root.childNodes[0].childNodes[1]
-                and root.childNodes[1].childNodes[1].nextSibling is None
-                and root.childNodes[1].childNodes[1].previousSibling
-                        is root.childNodes[1].childNodes[0]
-                and root.childNodes[1].childNodes[0].previousSibling is None
-                and root.childNodes[1].childNodes[0].nextSibling
-                        is root.childNodes[1].childNodes[1]
-                , "testNormalize2 -- sibling pointers")
-        doc.unlink()
-
-
-    def testBug0777884(self):
-        doc = parseString("text")
-        text = doc.documentElement.childNodes[0]
-        self.assertEqual(text.nodeType, Node.TEXT_NODE)
-        # Should run quietly, doing nothing.
-        text.normalize()
-        doc.unlink()
-
-    def testBug1433694(self):
-        doc = parseString("t")
-        node = doc.documentElement
-        node.childNodes[1].nodeValue = ""
-        node.normalize()
-        self.confirm(node.childNodes[-1].nextSibling is None,
-                     "Final child's .nextSibling should be None")
-
     def testSiblings(self):
         doc = parseString("text?")
         root = doc.documentElement
@@ -1373,15 +967,6 @@ def testWholeText(self):
         self.checkWholeText(text, "cabd")
         self.checkWholeText(text2, "cabd")
 
-    def testPatch1094164(self):
-        doc = parseString("")
-        elem = doc.documentElement
-        e = elem.firstChild
-        self.confirm(e.parentNode is elem, "Before replaceChild()")
-        # Check that replacing a child with itself leaves the tree unchanged
-        elem.replaceChild(e, e)
-        self.confirm(e.parentNode is elem, "After replaceChild()")
-
     def testReplaceWholeText(self):
         def setup():
             doc = parseString("ad")
@@ -1612,15 +1197,6 @@ def testExceptionOnSpacesInXMLNSValue(self):
         with self.assertRaisesRegex(ValueError, 'Unsupported syntax'):
             parseString('')
 
-    def testDocRemoveChild(self):
-        doc = parse(tstfile)
-        title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
-        self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
-        num_children_before = len(doc.childNodes)
-        doc.removeChild(doc.childNodes[0])
-        num_children_after = len(doc.childNodes)
-        self.assertTrue(num_children_after == num_children_before - 1)
-
     def testProcessingInstructionNameError(self):
         # wrong variable in .nodeValue property will
         # lead to "NameError: name 'data' is not defined"
@@ -1663,5 +1239,441 @@ def test_cdata_parsing(self):
         dom2 = parseString(dom1.toprettyxml())
         self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '')
 
+    def test_toxml(self):
+        # simple test
+        xml_str = 'text
'
+        root = parseString(xml_str)
+        self.assertEqual(root.toxml(), 'text
')
+        self.assertEqual(
+            root.toxml(standalone=True),
+            'text
')
+        self.assertEqual(
+            root.toxml(standalone=False),
+            'text
')
+        self.assertEqual(
+            root.toxml(encoding="utf-8"),
+            b'text
')
+
+    def test_hasChildNodes(self):
+        """Test if a node has children."""
+        dom = parseString("")
+        doc = dom.documentElement
+        self.assertTrue(doc.hasChildNodes())
+        dom2 = parseString("")
+        doc2 = dom2.documentElement
+        self.assertFalse(doc2.hasChildNodes())
+
+    def test_childNodes(self):
+        """Test the list of children Nodes of a DOM Element."""
+        dom = parseString('')
+        doc = dom.documentElement
+        self.assertEqual(len(doc.childNodes), 2)
+        self.assertIsInstance(doc.childNodes[0], Element)
+        self.assertIsInstance(doc.childNodes, NodeList)
+
+    def test_firstChild(self):
+        """Test access to the first child of a DOM Element."""
+        dom = parseString('')
+        doc = dom.documentElement
+        self.assertEqual(doc.firstChild.toxml(), '
')
+        self.assertIsInstance(doc.firstChild, Element)
+        dom = parseString('')
+        doc = dom.documentElement
+        self.assertIsNone(doc.firstChild)
+
+    def test_lastChild(self):
+        """Test access to the last child of a DOM Element."""
+        dom = parseString('')
+        doc = dom.documentElement
+        self.assertEqual(doc.lastChild.toxml(), '
')
+        self.assertIsInstance(doc.lastChild, Element)
+        dom = parseString('')
+        doc = dom.documentElement
+        self.assertIsNone(doc.lastChild)
+
+    def test_insertBefore_with_document_fragment_node(self):
+        """Test insertBefore for DOCUMENT_FRAGMENT_NODE."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newNode = dom.createElement('new')
+        fragment = DocumentFragment()
+        fragment.appendChild(newNode)
+        # insertBefore with document fragment
+        parentNode.insertBefore(fragment, existingNode)
+        self.assertEqual(parentNode.toxml(),
+                         '')
+        self.assertIs(parentNode.firstChild, newNode)
+        self.assertEqual(parentNode.childNodes.length, 2)
+        # after the insert the fragment is empty
+        self.assertEqual(fragment.childNodes.length, 0)
+
+    def test_insertBefore_with_invalid_node_type(self):
+        """Test insertBefore with invalid node type."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        doc = getDOMImplementation().createDocument(None, "doc", None)
+        # parentNode.insertBefore(doc, existingNode) will raise
+        self.assertRaises(xml.dom.HierarchyRequestErr,
+                          parentNode.insertBefore, doc, existingNode)
+
+    def test_insertBefore_with_text_node(self):
+        """Test insertBefore for text node."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newNode = dom.createTextNode('new text')
+        # insertBefore with text node
+        parentNode.insertBefore(newNode, existingNode)
+        self.assertEqual(parentNode.toxml(),
+                         'new text')
+
+    def test_insertBefore_with_no_reference_node(self):
+        """Test insertBefore with no reference node."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createTextNode('text trailing')
+        # insertBefore with no reference node aka None
+        parentNode.insertBefore(newNode, None)
+        self.assertEqual(parentNode.toxml(),
+                         'text trailing')
+
+    def test_insertBefore_with_no_children(self):
+        """Test insertBefore with no children node."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createElement('new')
+        existingNode = dom.createElement('existing')
+        # insertBefore with existing node not existing
+        self.assertRaises(xml.dom.NotFoundErr,
+                          parentNode.insertBefore, newNode, existingNode)
+
+    def test_insertBefore_return_value(self):
+        """Test insertBefore returned value."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createElement('new')
+        existingNode = parentNode.firstChild
+        return_value = parentNode.insertBefore(newNode, existingNode)
+        self.assertEqual(return_value, newNode)
+
+    def test_appendChild(self):
+        """Test appendChild for a simple node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createElement('new')
+        # append a new node child
+        parentNode.appendChild(newNode)
+        self.assertEqual(parentNode.toxml(),
+                         '')
+
+    def test_appendChild_with_document_fragment_node(self):
+        """Test appendChild for DOCUMENT_FRAGMENT_NODE."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createElement('new')
+        fragment = DocumentFragment()
+        fragment.appendChild(newNode)
+        # appendChild with document fragment
+        parentNode.appendChild(fragment)
+        self.assertEqual(parentNode.toxml(),
+                         '')
+        # after the appendChild, the fragment should be empty
+        self.assertEqual(fragment.childNodes.length, 0)
+
+    def test_appendChild_with_invalid_node_type(self):
+        """Test appendChild with invalid node type."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        doc = getDOMImplementation().createDocument(None, "doc", None)
+        # parentNode.appendChild(doc) will raise
+        self.assertRaises(xml.dom.HierarchyRequestErr,
+                          parentNode.appendChild, doc)
+
+    def test_appendChild_return_value(self):
+        """Test appendChild returned value."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        newNode = dom.createElement('new')
+        # append a new node child
+        return_value = parentNode.appendChild(newNode)
+        self.assertEqual(return_value, newNode)
+
+    def test_replaceChild(self):
+        """Test replaceChild for a simple node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newNode = dom.createElement('new')
+        # replace existingNode by newNode
+        parentNode.replaceChild(newNode, existingNode)
+        self.assertEqual(parentNode.toxml(), '')
+
+    def test_replaceChild_with_document_fragment_node(self):
+        """Test replaceChild for DOCUMENT_FRAGMENT_NODE."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newNode = dom.createElement('new')
+        fragment = DocumentFragment()
+        fragment.appendChild(newNode)
+        # replaceChild with document fragment
+        parentNode.replaceChild(fragment, existingNode)
+        self.assertEqual(parentNode.toxml(), '')
+        # after the replaceChild, the fragment should be empty
+        self.assertEqual(fragment.childNodes.length, 0)
+
+    def test_replaceChild_with_invalid_node_type(self):
+        """Test replaceChild with invalid node type."""
+        # Preparing the test
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        doc = getDOMImplementation().createDocument(None, "doc", None)
+        # parentNode.replaceChild(doc, existingNode) will raise
+        self.assertRaises(xml.dom.HierarchyRequestErr,
+                          parentNode.replaceChild, doc, existingNode)
+
+    def test_replaceChild_with_same_node(self):
+        """Test replaceChild with same node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        # replace existingNode by existingNode
+        parentNode.replaceChild(existingNode, existingNode)
+        self.assertEqual(parentNode.toxml(), '')
+
+    def test_replaceChild_with_new_child_parent_not_None(self):
+        """Test replaceChild with new child parent is not Nonce."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newdom = parseString('')
+        newparentNode = newdom.documentElement
+        newNode = newparentNode.firstChild
+        # replace existingNode by newNode
+        parentNode.replaceChild(newNode, existingNode)
+        self.assertEqual(parentNode.toxml(), '')
+
+    def test_replaceChild_with_no_existing_node(self):
+        """Test replaceChild with missing existing node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = dom.createElement('existing')
+        newdom = parseString('')
+        newparentNode = newdom.documentElement
+        newNode = newparentNode.firstChild
+        # replace existingNode by newNode
+        self.assertRaises(xml.dom.NotFoundErr,
+                          parentNode.replaceChild, newNode, existingNode)
+
+    def test_replaceChild_return_value(self):
+        """Test replaceChild returned value."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        newNode = dom.createElement('new')
+        # replace a new node child
+        return_value = parentNode.replaceChild(newNode, existingNode)
+        self.assertEqual(return_value, existingNode)
+
+    def test_removeChild(self):
+        """Test removeChild for a simple node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = parentNode.firstChild
+        # remove an existing node child
+        parentNode.removeChild(existingNode)
+        self.assertEqual(parentNode.toxml(), '')
+
+    def test_removeChild_with_no_existing_node(self):
+        """Test removeChild with missing existing node."""
+        dom = parseString('')
+        parentNode = dom.documentElement
+        existingNode = dom.createElement('existing')
+        # remove non existing Node
+        self.assertRaises(xml.dom.NotFoundErr,
+                          parentNode.removeChild, existingNode)
+
+    def test_normalize_text_nodes(self):
+        """Test normalize two text nodes."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createTextNode("first"))
+        root.appendChild(doc.createTextNode("second"))
+        # Two text nodes have been created
+        # len(root.childNodes) == 2
+        # root.firstChild.data == 'first'
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 1)
+        self.assertEqual(root.firstChild.data, 'firstsecond')
+        self.assertEqual(root.toxml(), 'firstsecond')
+
+
+    def test_normalize_empty_text_node(self):
+        """Test normalize removing the childNode of an empty text node."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createTextNode(""))
+        # len(root.childNodes) == 1
+        # root.firstChild.data == ''
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 0)
+        self.assertIs(root.firstChild, None)
+        self.assertEqual(root.toxml(), '')
+
+
+    def test_normalize_text_node_and_element_node(self):
+        """Test normalize merges two text nodes but not element node."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createTextNode("first"))
+        root.appendChild(doc.createTextNode("second"))
+        root.appendChild(doc.createElement("element"))
+        # len(root.childNodes) == 3
+        # root.firstChild.data == 'first'
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 2)
+        self.assertEqual(root.firstChild.data, 'firstsecond')
+        self.assertEqual(root.toxml(), 'firstsecond')
+
+    def test_normalize_two_text_nodes_with_first_empty(self):
+        """Test that normalize removes the first empty text node only."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createTextNode(""))
+        root.appendChild(doc.createTextNode("second"))
+        # Two text nodes have been created
+        # len(root.childNodes) == 2
+        # root.firstChild.data == ''
+        # root.lastChild.data == 'second'
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 1)
+        self.assertEqual(root.firstChild.data, 'second')
+        self.assertEqual(root.toxml(), 'second')
+
+    def test_normalize_two_text_nodes_with_last_empty(self):
+        """Test that normalize removes the last empty text node only."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createTextNode("first"))
+        root.appendChild(doc.createTextNode(""))
+        # Two text nodes have been created
+        # len(root.childNodes) == 2
+        # root.firstChild.data == 'first'
+        # root.lastChild.data == ''
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 1)
+        self.assertEqual(root.firstChild.data, 'first')
+        self.assertEqual(root.toxml(), 'first')
+
+    def test_normalize_two_element_nodes_with_empty_text_node(self):
+        """Test that normalize removes the empty text node in between."""
+        doc = parseString("")
+        root = doc.documentElement
+        root.appendChild(doc.createElement('first'))
+        root.appendChild(doc.createTextNode(''))
+        root.appendChild(doc.createElement('second'))
+        # Two text nodes have been created
+        # len(root.childNodes) == 3
+        # root.toxml() == ''
+        doc.normalize()
+        self.assertEqual(len(root.childNodes), 2)
+        self.assertEqual(root.toxml(), '')
+
+    def text_normalize_nested_elements_with_text_nodes(self):
+        """Test that normalize is still working in a more complex tree."""
+        xml = 'texttext'
+        doc = parseString(xml)
+        root = doc.documentElement
+        root.childNodes[0].appendChild(doc.createTextNode('added'))
+        root.childNodes[1].appendChild(doc.createTextNode(''))
+        doc.normalize()
+        self.assertEqual(len(root.childNodes[0]), 1)
+        self.assertEqual(len(root.childNodes[1]), 1)
+        self.assertEqual(
+            root.toxml(),
+            'textaddedtext')
+
+    def test_normalize_on_text_node(self):
+        """Test that normalize on a text Node doesn't fail.
+
+        See https://bugs.python.org/issue777884
+        """
+        doc = parseString("text")
+        text = doc.documentElement.childNodes[0]
+        self.assertEqual(text.nodeType, Node.TEXT_NODE)
+        # Should run quietly, doing nothing.
+        text.normalize()
+
+    def test_normalize_unlink_child_node(self):
+        """Test that normalize unlinks empty child nodes.
+
+        See https://bugs.python.org/issue1433694
+        """
+        doc = parseString("text")
+        root = doc.documentElement
+        root.childNodes[1].nodeValue = ''
+        doc.normalize()
+        # Final child's .nextSibling should be None
+        self.assertIsNone(root.childNodes[-1].nextSibling)
+
+    def test_cloneNode_with_simple_element(self):
+        """Test cloneNode for a simple Element Node."""
+        doc = parseString("")
+        root = doc.documentElement
+        new_clone = root.cloneNode(deep=True)
+        self.assertEqual(new_clone.toxml(), root.toxml())
+        self.assertEqual(new_clone.nodeType, root.nodeType)
+        new_clone = root.cloneNode(deep=False)
+        self.assertEqual(new_clone.toxml(), '')
+
+    def test_cloneNode_with_text_node(self):
+        """Test cloneNode with text node."""
+        doc = parseString("text")
+        root = doc.documentElement
+        new_clone = root.cloneNode(deep=True)
+        self.assertEqual(new_clone.toxml(), root.toxml())
+        self.assertEqual(new_clone.nodeType, root.nodeType)
+        new_clone = root.cloneNode(deep=False)
+        self.assertEqual(new_clone.toxml(), '')
+
+    def test_cloneNode_with_text_and_element_nodes(self):
+        """Test cloneNode with text and element nodes."""
+        doc = parseString("text")
+        root = doc.documentElement
+        new_clone = root.cloneNode(deep=True)
+        self.assertEqual(new_clone.toxml(), root.toxml())
+        self.assertEqual(new_clone.nodeType, root.nodeType)
+        new_clone = root.cloneNode(deep=False)
+        self.assertEqual(new_clone.toxml(), '')
+
+    def test_cloneNode_with_element_node_with_attribute(self):
+        """Test cloneNode with element nodes with attribute."""
+        doc = parseString("")
+        root = doc.documentElement
+        new_clone = root.cloneNode(deep=True)
+        self.assertEqual(new_clone.toxml(), root.toxml())
+        self.assertEqual(new_clone.nodeType, root.nodeType)
+        new_clone = root.cloneNode(deep=False)
+        self.assertEqual(new_clone.toxml(), '')
+
+    def test_cloneNode_parentNode_None(self):
+        """Test cloneNode that the parent node is None."""
+        doc = parseString("")
+        root = doc.documentElement
+        new_clone = root.firstChild.cloneNode(deep=True)
+        self.assertEqual(new_clone.toxml(), '')
+        self.assertIsNone(new_clone.parentNode)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py
index d09ef5e7d0371a..916c0503b3d39b 100644
--- a/Lib/xml/dom/minidom.py
+++ b/Lib/xml/dom/minidom.py
@@ -32,6 +32,7 @@
 
 
 class Node(xml.dom.Node):
+    """Define properties accessible on a DOM node."""
     namespaceURI = None # this is non-null only for elements and attributes
     parentNode = None
     ownerDocument = None
@@ -44,6 +45,7 @@ def __bool__(self):
         return True
 
     def toxml(self, encoding=None, standalone=None):
+        """Generate a string representation of a DOM."""
         return self.toprettyxml("", "", encoding, standalone)
 
     def toprettyxml(self, indent="\t", newl="\n", encoding=None,
@@ -66,6 +68,7 @@ def toprettyxml(self, indent="\t", newl="\n", encoding=None,
             return writer.detach().getvalue()
 
     def hasChildNodes(self):
+        """Return True if the node has children, False else."""
         return bool(self.childNodes)
 
     def _get_childNodes(self):
@@ -80,10 +83,17 @@ def _get_lastChild(self):
             return self.childNodes[-1]
 
     def insertBefore(self, newChild, refChild):
+        """Insert a new DOM Node before an existing Node.
+
+        https://dom.spec.whatwg.org/#dom-node-insertbefore
+        The insertBefore(node, child) method, when invoked,
+        must return the result of pre-inserting node into
+        this before child.
+        See also https://dom.spec.whatwg.org/#concept-node-pre-insert
+        """
         if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
             for c in tuple(newChild.childNodes):
                 self.insertBefore(c, refChild)
-            ### The DOM does not clearly specify what to return in this case
             return newChild
         if newChild.nodeType not in self._child_node_types:
             raise xml.dom.HierarchyRequestErr(
@@ -112,10 +122,10 @@ def insertBefore(self, newChild, refChild):
         return newChild
 
     def appendChild(self, node):
+        """Append a child node to an existing node."""
         if node.nodeType == self.DOCUMENT_FRAGMENT_NODE:
             for c in tuple(node.childNodes):
                 self.appendChild(c)
-            ### The DOM does not clearly specify what to return in this case
             return node
         if node.nodeType not in self._child_node_types:
             raise xml.dom.HierarchyRequestErr(
@@ -129,6 +139,7 @@ def appendChild(self, node):
         return node
 
     def replaceChild(self, newChild, oldChild):
+        """Replace an existing node with a new node."""
         if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
             refChild = oldChild.nextSibling
             self.removeChild(oldChild)
@@ -161,6 +172,7 @@ def replaceChild(self, newChild, oldChild):
         return oldChild
 
     def removeChild(self, oldChild):
+        """Remove an existing child."""
         try:
             self.childNodes.remove(oldChild)
         except ValueError:
@@ -172,11 +184,16 @@ def removeChild(self, oldChild):
         oldChild.nextSibling = oldChild.previousSibling = None
         if oldChild.nodeType in _nodeTypes_with_children:
             _clear_id_cache(self)
-
         oldChild.parentNode = None
         return oldChild
 
     def normalize(self):
+        """Transform a node into its normalized form.
+
+        Removes empty exclusive Text nodes and concatenates the data of
+        remaining contiguous exclusive Text nodes into the first of
+        their nodes.
+        """
         L = []
         for child in self.childNodes:
             if child.nodeType == Node.TEXT_NODE:
@@ -204,6 +221,7 @@ def normalize(self):
         self.childNodes[:] = L
 
     def cloneNode(self, deep):
+        """The Node.cloneNode() method returns a duplicate of the node."""
         return _clone_node(self, deep, self.ownerDocument or self)
 
     def isSupported(self, feature, version):
@@ -864,7 +882,7 @@ def getElementsByTagNameNS(self, namespaceURI, localName):
             self, namespaceURI, localName, NodeList())
 
     def __repr__(self):
-        return "" % (self.tagName, id(self))
+        return f""
 
     def writexml(self, writer, indent="", addindent="", newl=""):
         """Write an XML element to a file-like object
@@ -1323,6 +1341,7 @@ def _get_internalSubset(self):
         return self.internalSubset
 
     def cloneNode(self, deep):
+        """The Node.cloneNode() method returns a duplicate of the node."""
         if self.ownerDocument is None:
             # it's ok
             clone = DocumentType(None)
@@ -1886,7 +1905,9 @@ def renameNode(self, n, namespaceURI, name):
 
 def _clone_node(node, deep, newOwnerDocument):
     """
-    Clone a node and give it the new owner document.
+    Returns a copy of node.
+
+    If deep is true, the copy also includes the node’s descendants.
     Called by Node.cloneNode and Document.importNode
     """
     if node.ownerDocument.isSameNode(newOwnerDocument):