0

We use VS2019 for the project. We have some unit tests like this:

// Arrange
var expectedXml =
    "<?xml version=\"1.0\" encoding=\"utf-16\"?><LeadData xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><LanguagePreference>English</LanguagePreference>...</LeadData>";

// Act
var xmlString = contract.ToXmlString();

// Assert
Assert.AreEqual(expectedXml, xmlString);

ToXmlString() is something like this:

var xmlSerializer = new XmlSerializer(this.GetType());
var stringWriter = new StringWriter();
var xmlWriter = new XmlTextWriter(stringWriter) { Formatting = Formatting.None };

xmlSerializer.Serialize(xmlWriter, this);
return stringWriter.ToString();

The unit test passed in VS2019. It targets .net framework 4.5.1.

However, if we use VS2022, although still targets .net framework 4.5.1, the unit test failed. It generated xml string is:

"<?xml version=\"1.0\" encoding=\"utf-16\"?><LeadData xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><LanguagePreference>English</LanguagePreference>...</LeadData>";

Somehow, xmlns:xsd and xmlns:xsi changed the order.

Anyone know why is this? Thanks.

Steve
  • 213,761
  • 22
  • 232
  • 286
urlreader
  • 6,319
  • 7
  • 57
  • 91
  • 2
    You are comparing a big huge magic string. Make your asserts "more precise". Loop over the names spaces in expected..and make sure it exists in result. Magic string equality checking in unit-tests are notoriously fragile. – granadaCoder Jun 27 '23 at 17:56
  • 2
    Order does not make a difference. – jdweng Jun 27 '23 at 18:27
  • 1
    Does this answer your question? [In XML, is the attribute order important?](https://stackoverflow.com/questions/33746224/in-xml-is-the-attribute-order-important) – Charlieface Jun 27 '23 at 23:55
  • 1
    Theroetically you could get a different order even in the same run of the application, and it would still be to spec. The order is *defined* as being irrelevant – Charlieface Jun 27 '23 at 23:55
  • thanks @Charlieface , I have to change the code to reorder the namespace and nodes. – urlreader Jun 28 '23 at 16:36

3 Answers3

2

The order of the namespace declarations and other serialization details are not guaranteed to stay the same across releases.

Instead parse and re-serialize your expectedXml using the same serializer you use for your test data. Or perform a semantic comparison of the XML for equality, eg: Comparing XmlDocument for equality (content wise)

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • it is the same serializer, but in VS2019 & 2022, they have different order. – urlreader Jun 27 '23 at 17:37
  • 1
    Not the same version of the serializer. If you serialize them both at runtime, they will most likely match. – David Browne - Microsoft Jun 27 '23 at 18:13
  • is there a way to 'force' them to use a version? – urlreader Jun 27 '23 at 18:19
  • There's only one version loaded by your unit test. – David Browne - Microsoft Jun 27 '23 at 18:21
  • I mean can we force it to load a specific version. for example, it works on my local, I can see it uses 'Framework\.NETFramework\v4.5.1\System.Xml.dll'. is there a way to force it use this on any machine, and if it does not have this dll on the machine, just fail. is it possible? – urlreader Jun 27 '23 at 18:26
  • 1
    No. The .NET Framework is baked into Windows. If you support multiple Windows versions and patch levels you need to support the corresponding .NET Framework versions. For .NET Core (aka .NET 5 and later) you can ship and control all the versions. – David Browne - Microsoft Jun 27 '23 at 18:34
0

The problem is the namespace may have different order, so we need to order them before compare:

public static bool DeepCompareXml(this string resultString, string testString)
{
    // reorder the namespace in the XML
    var result = System.Xml.Linq.XDocument.Parse(resultString);
    var originalAttributes = result.Root.Attributes();
    var orderedAttributes = originalAttributes.OrderBy(x => x.Name.ToString());
    result.Root.ReplaceAttributes(orderedAttributes);

    var test = System.Xml.Linq.XDocument.Parse(testString);
    originalAttributes = test.Root.Attributes();
    orderedAttributes = originalAttributes.OrderBy(x => x.Name.ToString());
    test.Root.ReplaceAttributes(orderedAttributes);

    var ret = System.Xml.Linq.XNode.DeepEquals(result, test);
    return ret;
}

The code used @brz post at c# XML Serialization: Order of namespace declarations

so the test becomes:

// Assert
Assert.AreEqual(expectedXml.DeepCompareXml(xmlString), true);

Now, it works fine. Thanks all.

urlreader
  • 6,319
  • 7
  • 57
  • 91
0

If you want to compare two XML documents, you should canonicalize both XML documents first. This will remove all non-important differences (like attribute order, or reundant xmlns attributes) but the meaning of both documents will be unchanged. See Canonical XML v1.1

There are libraries available in Java and C# that will do this for you with minimal effort.

kimbert
  • 2,376
  • 1
  • 10
  • 20