Skip to content

Instantly share code, notes, and snippets.

@pasenor
Last active December 8, 2015 18:36
Show Gist options
  • Save pasenor/309ff80542fb67966b58 to your computer and use it in GitHub Desktop.
Save pasenor/309ff80542fb67966b58 to your computer and use it in GitHub Desktop.
PartialEntityMatcher.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "AssertionError",
"evalue": "\nExpected: Object equal to given Entity, ignoring fields \n but: \nMissing fields: d\nUnexpected fields: a\nUnequal fields: \n\tb: 5 != 2\n",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-4-386e59c15430>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 69\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mPartialEntityMatcher\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mother_entity\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 70\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 71\u001b[1;33m \u001b[0massert_that\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0me1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mis_equal_to_entity\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0me2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32m/home/gfrolov/venvs/rueabe/local/lib/python2.7/site-packages/hamcrest/core/assert_that.pyc\u001b[0m in \u001b[0;36massert_that\u001b[1;34m(arg1, arg2, arg3)\u001b[0m\n\u001b[0;32m 41\u001b[0m \"\"\"\n\u001b[0;32m 42\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marg2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mMatcher\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 43\u001b[1;33m \u001b[0m_assert_match\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mactual\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmatcher\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mreason\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 44\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 45\u001b[0m \u001b[0m_assert_bool\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0massertion\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mreason\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0marg2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m/home/gfrolov/venvs/rueabe/local/lib/python2.7/site-packages/hamcrest/core/assert_that.pyc\u001b[0m in \u001b[0;36m_assert_match\u001b[1;34m(actual, matcher, reason)\u001b[0m\n\u001b[0;32m 55\u001b[0m \u001b[0mmatcher\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdescribe_mismatch\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mactual\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdescription\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 56\u001b[0m \u001b[0mdescription\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mappend_text\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'\\n'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 57\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mAssertionError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdescription\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 58\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 59\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mAssertionError\u001b[0m: \nExpected: Object equal to given Entity, ignoring fields \n but: \nMissing fields: d\nUnexpected fields: a\nUnequal fields: \n\tb: 5 != 2\n"
]
}
],
"source": [
"from hamcrest.core.base_matcher import BaseMatcher\n",
"from hamcrest import assert_that\n",
"\n",
"\n",
"class PartialEntityMatcher(BaseMatcher):\n",
" def __init__(self, entity, fields_to_skip):\n",
" self.entity = entity\n",
" self.fields_to_skip = fields_to_skip\n",
"\n",
" self.missing_fields = set()\n",
" self.extra_fields = set()\n",
" self.unequal_fields = dict()\n",
"\n",
" def _matches(self, other_entity):\n",
" fields_of_self = set(self.entity.__dict__.keys())\n",
" fields_of_other = set(other_entity.__dict__.keys())\n",
"\n",
" self.missing_fields = fields_of_self.difference(fields_of_other)\n",
" self.extra_fields = fields_of_other.difference(fields_of_self)\n",
"\n",
" for field_name in fields_of_self.intersection(fields_of_other):\n",
" if field_name in self.fields_to_skip:\n",
" continue\n",
" field_of_self = getattr(self.entity, field_name)\n",
" field_of_other = getattr(other_entity, field_name)\n",
" if isinstance(field_of_self, list):\n",
" field_of_self = sorted(field_of_self)\n",
" field_of_other = sorted(field_of_other)\n",
" if field_of_self != field_of_other:\n",
" self.unequal_fields[field_name] = (field_of_self, field_of_other)\n",
"\n",
" if self.unequal_fields or self.missing_fields or self.extra_fields:\n",
" return False\n",
" return True\n",
"\n",
" def describe_mismatch(self, item, mismatch_description):\n",
" if self.missing_fields:\n",
" mismatch_description.append_text(\n",
" '\\nMissing fields: {}'.format(', '.join(self.missing_fields))\n",
" )\n",
" if self.extra_fields:\n",
" mismatch_description.append_text(\n",
" '\\nUnexpected fields: {}'.format(', '.join(self.extra_fields))\n",
" )\n",
" if self.unequal_fields:\n",
" diff_description = '\\n\\t'.join(\n",
" '{}: {} != {}'.format(key, self_value, other_value)\n",
" for key, (self_value, other_value) in self.unequal_fields.iteritems()\n",
" )\n",
" mismatch_description.append_text('\\nUnequal fields: \\n\\t{}'.format(diff_description))\n",
"\n",
" def describe_to(self, description):\n",
" description.append_text(\n",
" 'Object equal to given {}, ignoring fields {}'.format(\n",
" self.entity.__class__.__name__,\n",
" ', '.join(self.fields_to_skip)\n",
" )\n",
" )\n",
"\n",
"class Entity(object):\n",
" def __init__(self, **kwargs):\n",
" self.__dict__.update(kwargs)\n",
"\n",
"\n",
"e1 = Entity(a=1, b=2, c=3)\n",
"e2 = Entity(b=5, c=3, d=4)\n",
"\n",
"def is_equal_to_entity(other_entity):\n",
" return PartialEntityMatcher(other_entity, {})\n",
"\n",
"assert_that(e1, is_equal_to_entity(e2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Если вызываем как `assert_that(e1, is_equal_to_entity(e2))`, то подразумеваем\n",
"\n",
" \"missing\" == \"missing from e1\",\n",
"\n",
"т.е. поля, которые есть в e2, но не в e1\n",
"\n",
"В примере - поле `d`"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment