Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default match by name with differently sorted elements doesn't work #197

Closed
tavorjeremic opened this issue Sep 23, 2020 · 5 comments
Closed
Milestone

Comments

@tavorjeremic
Copy link

Hi,
I am struggling to create solution for matching elements by name and text and fall back to name if not found when elements have different order.

Given example in documentation (https://github.com/xmlunit/user-guide/wiki/SelectingNodes#using-more-than-one-elementselector-at-the-same-time):

new DefaultNodeMatcher(ElementSelectors.selectorForElementNamed("a", ElementSelectors.byNameAndText), ElementSelectors.byName)

Works for:

<root>
  <a>foo</a>
  <a>bar</a>
</root>
<root>
  <a>foo</a>
  <a>baz</a>
</root>

Causing TEXT_VALUE difference of second a element.

But if the order is different (so that first element is not matched using first selector):

<root>
  <a>bar</a>
  <a>foo</a>
</root>
<root>
  <a>foo</a>
  <a>baz</a>
</root>

This will match first element in control with first element in test causing 2 differences.

If we provide only byNameAndText selector then we get CHILD_LOOKUP instead of TEXT_VALUE difference.

Is there any way to match first by some condition (like byNameAndText) ALL elements (in both control and test) and then only to match by name what is left unmatched?

From what i gathered the issue is that comparison is trying to match first element in control with all from test using first selector, then using second selector and only then it goes to second element in control so either first element will be incorrectly matched or it will be marked as non existing instead of having different value.

@bodewig
Copy link
Member

bodewig commented Sep 24, 2020

Actually my intention was - and so far I assumed I had implemented it that way :-) - that using DefaultNodeMatcher the way you do would just work the way you expect. Looks as if I need to write a test, thanks!

@bodewig
Copy link
Member

bodewig commented Sep 26, 2020

hmm, the tests I've added pass and

import org.xmlunit.diff.*;
import org.xmlunit.builder.DiffBuilder;

public class Test197 {

    public static void main(String[] args) throws Exception {
        Diff ds = DiffBuilder.compare("<root><a>foo</a><a>bar</a></root>")
            .withTest("<root><a>baz</a><a>foo</a></root>")
            .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors
                    .selectorForElementNamed("a", ElementSelectors.byNameAndText),
                ElementSelectors.byName))
            .build();
        for (Difference d : ds.getDifferences()) {
            System.err.println(d.toString());
        }
    }
}

prints

Expected child nodelist sequence '0' but was '1' - comparing <a...> at /root[1]/a[1] to <a...> at /root[1]/a[2] (SIMILAR)
Expected child nodelist sequence '1' but was '0' - comparing <a...> at /root[1]/a[2] to <a...> at /root[1]/a[1] (SIMILAR)
Expected text value 'bar' but was 'baz' - comparing <a ...>bar</a> at /root[1]/a[2]/text()[1] to <a ...>baz</a> at /root[1]/a[1]/text()[1] (DIFFERENT)

which is what I expected. Something must be different in your case.

@tavorjeremic
Copy link
Author

Yes, this is because first element in control is found, but if you switch control and test:

Diff ds = DiffBuilder.compare("<root><a>baz</a><a>foo</a></root>")
                .withTest("<root><a>foo</a><a>bar</a></root>")
                .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors
                        .selectorForElementNamed("a", ElementSelectors.byNameAndText),
                        ElementSelectors.byName))
                .build();
        for (Difference d : ds.getDifferences()) {
            System.err.println(d.toString());
        }

Prints out:

Expected text value 'baz' but was 'foo' - comparing <a ...>baz</a> at /root[1]/a[1]/text()[1] to <a ...>foo</a> at /root[1]/a[1]/text()[1] (DIFFERENT)
Expected text value 'foo' but was 'bar' - comparing <a ...>foo</a> at /root[1]/a[2]/text()[1] to <a ...>bar</a> at /root[1]/a[2]/text()[1] (DIFFERENT)

@bodewig
Copy link
Member

bodewig commented Sep 27, 2020

Ah, good catch, thank you.

@bodewig bodewig added this to the 2.8.0 milestone Sep 27, 2020
@bodewig
Copy link
Member

bodewig commented Sep 27, 2020

should be fixed in the main branch, I'll publish 2.8.0-SNAPSHOT builds in a few minutes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants