diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index a2cb406138320f091aa133786f96565ba9be683b..3bc2dbf7f16a41738c843ccf45ff556895c2e43b 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -455,6 +455,27 @@ ffi.metatype( knot_rrset_t, {
 			C.free(dump[0])
 			return result
 		end,
+		txt_fields = function(rr, i)
+			assert(ffi.istype(knot_rrset_t, rr))
+			assert(i >= 0 and i < rr:rdcount())
+			local bufsize = 1024
+			local dump = ffi.new('char *', C.malloc(bufsize))
+			ffi.gc(dump, C.free)
+
+			local ret = knot.knot_rrset_txt_dump_data(rr, i, dump, 1024,
+							knot.KNOT_DUMP_STYLE_DEFAULT)
+			if ret >= 0 then
+				local out = {}
+				out.owner = dname2str(rr:owner())
+				out.ttl = rr:ttl()
+				out.class = kres.tostring.class[rr:class()]
+				out.type = kres.tostring.type[rr.type]
+				out.rdata = ffi.string(dump, ret)
+				return out
+			else
+				panic('knot_rrset_txt_dump_data failure ' .. tostring(ret))
+			end
+		end,
 		-- Return RDATA count for this RR set
 		rdcount = function(rr)
 			assert(ffi.istype(knot_rrset_t, rr))
diff --git a/tests/config/test_utils.lua b/tests/config/test_utils.lua
index 5a7f3111b101e358b5f96b6dbcd714553af60fdb..93a937708c65e6e1ae8faab1d24e45908b68f222 100644
--- a/tests/config/test_utils.lua
+++ b/tests/config/test_utils.lua
@@ -38,21 +38,46 @@ function M.not_contains(table, value, message)
 	return contains(fail, pass, table, value, message)
 end
 
-local function rrset_to_texts(rr)
-	local rr_text = {}
-	for w in rr:txt_dump():gmatch("%S+") do table.insert(rr_text, w) end
-	return rr_text
+local function answer2table(pkt)
+	local got_answers = {}
+	local ans_rrs = pkt:rrsets(kres.section.ANSWER)
+	for i = 1, #ans_rrs do
+		rrs = ans_rrs[i]
+		for rri = 0, rrs:rdcount() - 1 do
+			local rr = ans_rrs[i]:txt_fields(rri)
+			got_answers[rr.owner] = got_answers[rr.owner] or {}
+			got_answers[rr.owner][rr.type] = got_answers[rr.owner][rr.type] or {}
+			table.insert(got_answers[rr.owner][rr.type], rr.rdata)
+			table.sort(got_answers[rr.owner][rr.type])
+		end
+	end
+	return got_answers
 end
+
 M.NODATA = -1
 -- Resolve a name and check the answer.  Do *not* return until finished.
 -- expected_rdata is one string or a table of strings in presentation format
--- (not tested beyond IP addresses; TODO: handle ordering somehow?)
 function M.check_answer(desc, qname, qtype, expected_rcode, expected_rdata)
-	if expected_rdata ~= nil and type(expected_rdata) ~= 'table' then
-		expected_rdata = { expected_rdata }
+	assert(type(qtype) == 'number')
+	local qtype_str = kres.tostring.type[qtype]
+	qname = string.lower(qname)
+
+	local expected_answer = {}
+	if expected_rdata ~= nil then
+		if type(expected_rdata) ~= 'table' then
+			expected_rdata = { expected_rdata }
+		end
+		if #expected_rdata > 0 then
+			table.sort(expected_rdata)
+			expected_answer = {
+				[qname] = {
+					[qtype_str] =
+						expected_rdata
+				}
+			}
+		end
 	end
 
-	local qtype_str = kres.tostring.type[qtype]
 	local wire_rcode = expected_rcode
 	if expected_rcode == kres.rcode.NOERROR and type(expected_rdata) == 'table'
 			and #expected_rdata == 0 then
@@ -71,16 +96,10 @@ function M.check_answer(desc, qname, qtype, expected_rcode, expected_rdata)
 		   desc ..': checking number of answers for ' .. qname .. ' ' .. qtype_str)
 
 		if expected_rdata then
-			local ans_rrs = pkt:rrsets(kres.section.ANSWER)
-			ok(#expected_rdata == #ans_rrs,
-				desc .. ': checking number of answer records for ' .. qname .. ' ' .. qtype_str)
-			for i = 1, #ans_rrs do
-				ok(rrset_to_texts(ans_rrs[i])[4] == expected_rdata[i],
-					desc .. ': checking rdata of answer for ' .. qname .. ' ' .. qtype_str)
-			end
+			same(expected_answer, answer2table(pkt), 'ANSWER section matches')
 		end
 		done = true
-		end
+	end
 	resolve(qname, qtype, kres.class.IN, {},
 		function(...)
 			local ok, err = xpcall(callback, debug.traceback, ...)