Order independent flag for arrays
Have a flag to indicate that the order of items in arrays do not matter.
This is something I agree would be really great to have. I have thought about it before but there is one issue I don't know how to resolve:
You probably don't want something that makes all array comparisons order independent. That would most likely lead to subtle false positives in your tests. I think it would be nicer to specifically mark which arrays should be compared order independently. However since we currently do all the comparisons directly on serde_json::Value types, we have no way of adding order independence annotations.
The only solution I see to that is creating our own JSON type with two variants for arrays: One where order matters, and one where it doesn't. If we did that we would probably also want our own json! macro. That is quite a bit of setup 😕
Do you have any other ideas?
Hello,
This would be extremely helpful, even without all the setup needed to build the whole things. If it is a comparison mode, then someone opting in is expected to know what they are doing by reading the documentation.
I have exactly this kind of issue here, where my tests sudenly stopped working because the system has decided to return items on a first level array in a different order.
I suggest the following: with a new comparison mode, at first all arrays would be compared independently of the order (opt in for everything). If someone needs to take care of some of the arrays (e.e if these arrays need to be compared taking sorting in consideration) then a second call to assert_json_eq with the default mode (sorting matters) can be called passing only the sub arrays from both sides to be compared.
What I think could be a good addition is passing a json path (or a serde_json pointer) together with the two values to restrict comparison, so that sub objects can be selected in place.
Still, with an clear opt-in by means of a new comparison mode, I think that the implementation for all in adds enough value forusers.
I don't have time to work in this at the moment but PRs are much appreciated!
Hello,
Please see if you agree on the proposal.
If so, I might try to come with a solution.
Proposal
Create an assert_json_contains!(container: Value, contained: Value) macro.
Description
This macro will verify if container contains contained.
A container is considered to contain a contained if the following set of rules can be applied recursively:
- all keys on contained are found in container.
- All values of keys in contained are contained by the equivalent values of the container keys.
A value is considered to be contained if:
| type | rule |
|---|---|
| null | container value is null |
| numeric | container value is equals contained value, considering numeric mode |
| string | container value is equalscontained value, case included |
| object |
|
| array | all items on contained array are found (using these same rules) on container array as many times as they are present on contained array |
Examples
null
- a = null
- b = null
- c = []
- a contains b == true
- b contains a == true
- a contains c, b contains c, c contains a, c contains b == false
numeric
- a = 1
- b = 1
- c = 2
- a contains b == true
- b contains a == true
- a contains c, b contains c, c contains a, c contains b == false
string
- a = "n"
- b = "n"
- c = "o"
- d = "O"
- a contains b == true
- b contains a == true
- a contains c, b contains c, c contains a, c contains b == false
- c contains d, d contains c == false
Object
-
a = {"b": "c", "d": 1}
-
b = {"b": "c"}
-
c = {"b": "f"}
-
a contains b because all keys in b are found in a and all values from keys in b are contained by the equivalent values on a.
-
b does not contain a because a has an extra key that b does not have.
-
Neither a nor b contains c, because although all keys in c are present in a and b the value of keys in c are not contained by the equivalent values of these keys in a and b.
-
c does not contain b because values on c keys are not contained by values on the equivalent keys on b.
-
c does not contain a because a has keys that c does not have and because the keys matching keys on a do not have their values contained by the correspondend values on a.
arrays
-
a = [1, 2, 3]
-
b = [2, 3, 1]
-
c = [1, 2, 3, 5]
-
d = [1, 1, 2, 3]
-
e = [1, 2, 3, 1]
-
a contains b because all values on a are found in b. By found, understand that for each item the appropriate set of rules has been aplied.
-
b contains a because all values on a are found in b. By found, understand that for each item the appropriate set of rules has been aplied.
-
neither a nor b contain c, because c has at least one value not present neither in a nor b.
-
c contains both a and b, because all values on a and b are found in c.
-
a, b and c do not contain d because d has an item (the repeated 1) that none of them contain twice.
-
d contains a and b, because both a and b have their values found on d.
-
e contains d and d contains e because all values found on one are present in the other.
With all of that, a strict equality but not considering orders in lists might be obtained if obj1 contains obj2 and obj2 contains obj1.
If we do that we do not break the current API and give a good way for people trying to match service responses which are not expected to return a determined order on itens list to proceed.
What do you think?
Yes I think that makes sense and would be fairly straight forward to implement, its essentially how it works today except that arrays care about ordering.
The hard part, like mentioned above, is mixing array dependent ordering with independent. What if for parts of your JSON you care about ordering and other parts you don't? I've found that to be fairly common in practice.
Yup this is why creating another method would solve the problem.
From a philosophical point of view, strict equality should take order in consideration, because JSON specification enforses order on array, which it does not on fields in objects.
However, if you think about it, [1, 2, 3] contains [2, 3, 1] if 1 2 and 3 are contained.
By offering a way of "pure" contains, we cover yet another scenarios.
What if you have mixed requirements?
Then you will need to perform a contains operation to make sure that objects match and then a comparison of the strict ordering sub objects by equality or diuff.
I will try nto implement it and submit a pr.
See pull request #27