diff --git a/tests-extra/tests/dnssec/none_to_nsec/test.py b/tests-extra/tests/dnssec/none_to_nsec/test.py
index 28e42254cd6e9cd681c099fa47f763a30a9456b6..97ea74b5bd086e37501115ae888c334ac7411eb7 100644
--- a/tests-extra/tests/dnssec/none_to_nsec/test.py
+++ b/tests-extra/tests/dnssec/none_to_nsec/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master)
+t.link(zone, master, slave)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -31,8 +34,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tests/dnssec/none_to_nsec3/test.py b/tests-extra/tests/dnssec/none_to_nsec3/test.py
index ef14647e76439db0a1c016535f2f0df65e016763..9cb95e290d4c9562a7cc7dbf80d1ee679efc4e97 100644
--- a/tests-extra/tests/dnssec/none_to_nsec3/test.py
+++ b/tests-extra/tests/dnssec/none_to_nsec3/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master)
+t.link(zone, master, slave)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -32,8 +35,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tests/dnssec/nsec3_to_nsec/test.py b/tests-extra/tests/dnssec/nsec3_to_nsec/test.py
index 7a58c6d3114d46d0b04fd129f79dd61771c17a36..9249d0c8059132bd013b4503d511339a1a0d2880 100644
--- a/tests-extra/tests/dnssec/nsec3_to_nsec/test.py
+++ b/tests-extra/tests/dnssec/nsec3_to_nsec/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master)
+t.link(zone, master, slave)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -32,8 +35,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
@@ -62,8 +66,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-master.zone_wait(zone, serial=new_serial)
-t.sleep(1)
+master.zone_wait(zone, new_serial)
+slave.zone_wait(zone, new_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tests/dnssec/nsec3_to_nsec_ddns/test.py b/tests-extra/tests/dnssec/nsec3_to_nsec_ddns/test.py
index 8419de68ae0fc1105bec96c1179ee899eb05f68f..e4fefac537466f77216fe993c2107b436fcc6f9b 100644
--- a/tests-extra/tests/dnssec/nsec3_to_nsec_ddns/test.py
+++ b/tests-extra/tests/dnssec/nsec3_to_nsec_ddns/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master, ddns=True)
+t.link(zone, master, slave, ddns=True)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -32,8 +35,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
@@ -61,8 +65,9 @@ update.delete(zone[0].name, "NSEC3PARAM")
 update.send("NOERROR")
 
 # Wait for changed zone and flush.
-master.zone_wait(zone, serial=new_serial)
-t.sleep(1)
+master.zone_wait(zone, new_serial)
+slave.zone_wait(zone, new_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tests/dnssec/nsec_to_nsec3/test.py b/tests-extra/tests/dnssec/nsec_to_nsec3/test.py
index 8515631f71e940b04d3cf9586a2b1345cef41010..bf4a1d0d2f63b66351e9b084cc57ff634b9c03b1 100644
--- a/tests-extra/tests/dnssec/nsec_to_nsec3/test.py
+++ b/tests-extra/tests/dnssec/nsec_to_nsec3/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master)
+t.link(zone, master, slave)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -31,8 +34,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
@@ -61,8 +65,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-master.zone_wait(zone, serial=new_serial)
-t.sleep(1)
+master.zone_wait(zone, new_serial)
+slave.zone_wait(zone, new_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tests/dnssec/nsec_to_nsec3_ddns/test.py b/tests-extra/tests/dnssec/nsec_to_nsec3_ddns/test.py
index e040a867fd1089dd3c9a71231984f6349bd886eb..26035defd767fe1ebd012e14deb3fce86b4980b4 100644
--- a/tests-extra/tests/dnssec/nsec_to_nsec3_ddns/test.py
+++ b/tests-extra/tests/dnssec/nsec_to_nsec3_ddns/test.py
@@ -8,13 +8,16 @@ from dnstest.test import Test
 t = Test()
 
 master = t.server("knot")
+slave = t.server("bind")
 zone = t.zone_rnd(1, dnssec=False)
-t.link(zone, master, ddns=True)
+t.link(zone, master, slave, ddns=True)
 
 t.start()
 
 # Wait for listening server with unsigned zone.
 old_serial = master.zone_wait(zone)
+slave.zone_wait(zone)
+t.xfr_diff(master, slave, zone)
 
 # Check NSEC absence.
 master.check_nsec(zone, nonsec=True)
@@ -31,8 +34,9 @@ master.gen_confile()
 master.start()
 
 # Wait for changed zone and flush.
-new_serial = master.zone_wait(zone, serial=old_serial)
-t.sleep(1)
+new_serial = master.zone_wait(zone, old_serial)
+slave.zone_wait(zone, old_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
@@ -60,8 +64,9 @@ update.add(zone[0].name, 0, "NSEC3PARAM", "1 0 2 fedcba")
 update.send("NOERROR")
 
 # Wait for changed zone and flush.
-master.zone_wait(zone, serial=new_serial)
-t.sleep(1)
+master.zone_wait(zone, new_serial)
+slave.zone_wait(zone, new_serial)
+t.xfr_diff(master, slave, zone)
 master.flush()
 t.sleep(1)
 
diff --git a/tests-extra/tools/dnstest/zonefile.py b/tests-extra/tools/dnstest/zonefile.py
index 54ae7b9341312a16e530730dd4d33f97880a3865..967d76d6ca35dd7c4d78346a4f1fd658694d54c6 100644
--- a/tests-extra/tools/dnstest/zonefile.py
+++ b/tests-extra/tools/dnstest/zonefile.py
@@ -129,6 +129,8 @@ class ZoneFile(object):
         with open(self.path, "a") as file:
             file.write("@ 0 NSEC3PARAM 1 0 %i %s" % (iters, salt))
 
+        self.update_serial()
+
     def disable_nsec3(self):
         '''Remove NSEC3PARAM record if any.'''
 
@@ -142,6 +144,8 @@ class ZoneFile(object):
 
         os.remove(old_name)
 
+        self.update_serial()
+
     def backup(self):
         '''Make a backup copy of the actual zone file.'''
 
@@ -150,3 +154,26 @@ class ZoneFile(object):
             self.backup_num += 1
         except:
             raise Exception("Can't make a copy of zone file %s" % self.path)
+
+    def update_serial(self, new_serial=None):
+        '''Change SOA serial.'''
+
+        serial = None
+        first = False
+
+        old_name = self.path + ".old"
+        os.rename(self.path, old_name)
+
+        with open(old_name) as old_file, open(self.path, 'w') as new_file:
+            for line in old_file:
+                if "SOA" in line and not first:
+                    items = line.split()
+                    serial = int(items[-5])
+                    items[-5] = str(serial + 1) if not new_serial else str(new_serial)
+                    new_file.write(str.join(" ", items))
+                    new_file.write("\n")
+                    first = True
+                else:
+                    new_file.write(line)
+
+        os.remove(old_name)
diff --git a/tests-extra/tools/zone_generate.py b/tests-extra/tools/zone_generate.py
index 99ac7383808d8c2de3018e35653c15a0b2408eb6..0f6fd08eab59af4a684dd12e871b3451df522b20 100755
--- a/tests-extra/tools/zone_generate.py
+++ b/tests-extra/tools/zone_generate.py
@@ -369,7 +369,7 @@ def gen_soa(origin, serial, auth = None):
     soa =  ''
     soa += '$TTL %d\n' % TTL
     s = '@ IN SOA %s %s' % (g_fqdn('ns'), g_fqdn('username'))
-    s += '( %s %d %d %s %s )\n' % (serial, refresh, refresh * 3, '4w', '1h' )
+    s += ' %s %d %d %s %s\n' % (serial, refresh, refresh * 3, '4w', '1h' )
     if auth != None:
         if auth != '.':
             auth += '.'