diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy
new file mode 100644
index 0000000000..c0c8c98392
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/MarkupBuilderUnitTest.groovy
@@ -0,0 +1,40 @@
+package com.baeldung.xml
+
+import groovy.xml.MarkupBuilder
+import groovy.xml.XmlUtil
+import spock.lang.Specification
+
+class MarkupBuilderUnitTest extends Specification {
+
+ def xmlFile = getClass().getResource("articles_short_formatted.xml")
+
+def "Should create XML properly"() {
+ given: "Node structures"
+
+ when: "Using MarkupBuilderUnitTest to create com.baeldung.xml structure"
+ def writer = new StringWriter()
+ new MarkupBuilder(writer).articles {
+ article {
+ title('First steps in Java')
+ author(id: '1') {
+ firstname('Siena')
+ lastname('Kerr')
+ }
+ 'release-date'('2018-12-01')
+ }
+ article {
+ title('Dockerize your SpringBoot application')
+ author(id: '2') {
+ firstname('Jonas')
+ lastname('Lugo')
+ }
+ 'release-date'('2018-12-01')
+ }
+ }
+
+ then: "Xml is created properly"
+ XmlUtil.serialize(writer.toString()) == XmlUtil.serialize(xmlFile.text)
+}
+
+
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy
new file mode 100644
index 0000000000..ada47406a1
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlParserUnitTest.groovy
@@ -0,0 +1,94 @@
+package com.baeldung.xml
+
+
+import spock.lang.Shared
+import spock.lang.Specification
+
+class XmlParserUnitTest extends Specification {
+
+ def xmlFile = getClass().getResourceAsStream("articles.xml")
+
+ @Shared
+ def parser = new XmlParser()
+
+ def "Should read XML file properly"() {
+ given: "XML file"
+
+ when: "Using XmlParser to read file"
+ def articles = parser.parse(xmlFile)
+
+ then: "Xml is loaded properly"
+ articles.'*'.size() == 4
+ articles.article[0].author.firstname.text() == "Siena"
+ articles.article[2].'release-date'.text() == "2018-06-12"
+ articles.article[3].title.text() == "Java 12 insights"
+ articles.article.find { it.author.'@id'.text() == "3" }.author.firstname.text() == "Daniele"
+ }
+
+
+ def "Should add node to existing com.baeldung.xml using NodeBuilder"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ def articleNode = new NodeBuilder().article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ articles.append(articleNode)
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 5
+ articles.article[4].title.text() == "Traversing XML in the nutshell"
+ }
+
+ def "Should replace node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ def articleNode = new NodeBuilder().article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ articles.article[0].replaceNode(articleNode)
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 4
+ articles.article[0].title.text() == "Traversing XML in the nutshell"
+ }
+
+ def "Should modify node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Changing value of one of the nodes"
+ articles.article.each { it.'release-date'[0].value = "2019-05-18" }
+
+ then: "XML is updated"
+ articles.article.findAll { it.'release-date'.text() != "2019-05-18" }.isEmpty()
+ }
+
+ def "Should remove article from com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Removing all articles but with id==3"
+ articles.article
+ .findAll { it.author.'@id'.text() != "3" }
+ .each { articles.remove(it) }
+
+ then: "There is only one article left"
+ articles.children().size() == 1
+ articles.article[0].author.'@id'.text() == "3"
+ }
+
+}
diff --git a/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy
new file mode 100644
index 0000000000..ffeaa46fce
--- /dev/null
+++ b/core-groovy-2/src/test/groovy/com/baeldung/xml/XmlSlurperUnitTest.groovy
@@ -0,0 +1,102 @@
+package com.baeldung.xml
+
+
+import groovy.xml.XmlUtil
+import spock.lang.Shared
+import spock.lang.Specification
+
+class XmlSlurperUnitTest extends Specification {
+
+ def xmlFile = getClass().getResourceAsStream("articles.xml")
+
+ @Shared
+ def parser = new XmlSlurper()
+
+ def "Should read XML file properly"() {
+ given: "XML file"
+
+ when: "Using XmlSlurper to read file"
+ def articles = parser.parse(xmlFile)
+
+ then: "Xml is loaded properly"
+ articles.'*'.size() == 4
+ articles.article[0].author.firstname == "Siena"
+ articles.article[2].'release-date' == "2018-06-12"
+ articles.article[3].title == "Java 12 insights"
+ articles.article.find { it.author.'@id' == "3" }.author.firstname == "Daniele"
+ }
+
+ def "Should add node to existing com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Adding node to com.baeldung.xml"
+ articles.appendNode {
+ article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ }
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "Node is added to com.baeldung.xml properly"
+ articles.'*'.size() == 5
+ articles.article[4].title == "Traversing XML in the nutshell"
+ }
+
+ def "Should modify node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Changing value of one of the nodes"
+ articles.article.each { it.'release-date' = "2019-05-18" }
+
+ then: "XML is updated"
+ articles.article.findAll { it.'release-date' != "2019-05-18" }.isEmpty()
+ }
+
+ def "Should replace node"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Replacing node"
+ articles.article[0].replaceNode {
+ article(id: '5') {
+ title('Traversing XML in the nutshell')
+ author {
+ firstname('Martin')
+ lastname('Schmidt')
+ }
+ 'release-date'('2019-05-18')
+ }
+ }
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "Node is replaced properly"
+ articles.'*'.size() == 4
+ articles.article[0].title == "Traversing XML in the nutshell"
+ }
+
+ def "Should remove article from com.baeldung.xml"() {
+ given: "XML object"
+ def articles = parser.parse(xmlFile)
+
+ when: "Removing all articles but with id==3"
+ articles.article
+ .findAll { it.author.'@id' != "3" }
+ .replaceNode {}
+
+ articles = parser.parseText(XmlUtil.serialize(articles))
+
+ then: "There is only one article left"
+ articles.children().size() == 1
+ articles.article[0].author.'@id' == "3"
+ }
+
+}
diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml
new file mode 100644
index 0000000000..ef057405f5
--- /dev/null
+++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles.xml
@@ -0,0 +1,34 @@
+
+
+ First steps in Java
+
+ Siena
+ Kerr
+
+ 2018-12-01
+
+
+ Dockerize your SpringBoot application
+
+ Jonas
+ Lugo
+
+ 2018-12-01
+
+
+ SpringBoot tutorial
+
+ Daniele
+ Ferguson
+
+ 2018-06-12
+
+
+ Java 12 insights
+
+ Siena
+ Kerr
+
+ 2018-07-22
+
+
diff --git a/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml
new file mode 100644
index 0000000000..6492020e03
--- /dev/null
+++ b/core-groovy-2/src/test/resources/com/baeldung/xml/articles_short_formatted.xml
@@ -0,0 +1,18 @@
+
+
+ First steps in Java
+
+ Siena
+ Kerr
+
+ 2018-12-01
+
+
+ Dockerize your SpringBoot application
+
+ Jonas
+ Lugo
+
+ 2018-12-01
+
+