PyHamcrest
PyHamcrest copied to clipboard
object comparison override
Hi there, I am new to Hamcrest and would like to know if there is an example to override the standard object comparison equal operator. I basically have to change the list comparison for the properties of the object otherwise the default one is order dependent. Cheers.
You question isn't really clear, I'm afraid. Most matchers can take nested matchers in addition to literal values, so I expect what you want is possible, but I'd need a little more information to help. Do you have an object whose property is a list, or is list of objects with properties?
This might be better asked on Stack Overflow.
Hello @brunns, yes apologies, let me do a concrete example to explain. I have an object type that has an inner attribute of type dict.
I just want to be able to compare the internal representation (it's a dict in a property called _inner) and nothing else to assume that two objects are equal.
Right now I do this:
assert_that(stix_obj, equal_to(return_obj))
The STIX object looks like this

The test fails with this two pairs of objects:
{"type": "intrusion-set", "spec_version": "2.1", "id": "intrusion-set--ed69450a-f067-4b51-9ba2-c4616b9a6713", "created": "2016-08-08T15:50:10.983Z", "modified": "2016-08-08T15:50:10.983Z", "name": "APT BPP", "description": "An advanced persistent threat that seeks to disrupt Branistan's election with multiple attacks.", "aliases": ["Bran-teaser"], "first_seen": "2016-01-08T12:50:40.123Z", "goals": ["Disrupt the BPP", "Influence the Branistan election"], "resource_level": "government", "primary_motivation": "ideology", "secondary_motivations": ["dominance"]}
and:
{"type": "intrusion-set", "spec_version": "2.1", "id": "intrusion-set--ed69450a-f067-4b51-9ba2-c4616b9a6713", "created": "2016-08-08T15:50:10.983Z", "modified": "2016-08-08T15:50:10.983Z", "name": "APT BPP", "description": "An advanced persistent threat that seeks to disrupt Branistan's election with multiple attacks.", "aliases": ["Bran-teaser"], "first_seen": "2016-01-08T12:50:40.123Z", "goals": ["Influence the Branistan election", "Disrupt the BPP"], "resource_level": "government", "primary_motivation": "ideology", "secondary_motivations": ["dominance"]}
This is because the dictionary property called goals can have a different order of strings.
So ideally I would like to create my own comparator that only considers the dict internal representation (_inner field) and then I guess based on the value types (like the list example) ignore the order.
I hope this makes sense now.
You can compose a custom matcher for this fairly easily:
import collections
from dataclasses import dataclass
from typing import Any, Dict
from hamcrest import assert_that, contains_inanyorder, has_entries, has_property
from hamcrest.core.matcher import Matcher
@dataclass
class Stix:
_inner: Dict
def is_nonstring_sequence(candidate: Any) -> bool:
return not isinstance(candidate, str) and isinstance(candidate, collections.abc.Sequence)
def equal_to_stix(expected: Stix) -> Matcher[Stix]:
return has_property(
"_inner",
has_entries(
**{
k: (contains_inanyorder(*v) if is_nonstring_sequence(v) else v)
for k, v in expected._inner.items()
}
),
)
def test_stix_comp():
actual = Stix(
{
"type": "intrusion-set",
"spec_version": "2.1",
"id": "intrusion-set--ed69450a-f067-4b51-9ba2-c4616b9a6713",
"created": "2016-08-08T15:50:10.983Z",
"modified": "2016-08-08T15:50:10.983Z",
"name": "APT BPP",
"description": "An advanced persistent threat that seeks to disrupt Branistan's election with multiple attacks.",
"aliases": ["Bran-teaser"],
"first_seen": "2016-01-08T12:50:40.123Z",
"goals": ["Disrupt the BPP", "Influence the Branistan election"],
"resource_level": "government",
"primary_motivation": "ideology",
"secondary_motivations": ["dominance"],
}
)
expected = Stix(
{
"type": "intrusion-set",
"spec_version": "2.1",
"id": "intrusion-set--ed69450a-f067-4b51-9ba2-c4616b9a6713",
"created": "2016-08-08T15:50:10.983Z",
"modified": "2016-08-08T15:50:10.983Z",
"name": "APT BPP",
"description": "An advanced persistent threat that seeks to disrupt Branistan's election with multiple attacks.",
"aliases": ["Bran-teaser"],
"first_seen": "2016-01-08T12:50:40.123Z",
"goals": ["Influence the Branistan election", "Disrupt the BPP"],
"resource_level": "government",
"primary_motivation": "ideology",
"secondary_motivations": ["dominance"],
}
)
assert_that(actual, equal_to_stix(expected))