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

py.test, assertion for assertDictContainsSubset? #2376

Open
tony opened this issue Apr 21, 2017 · 18 comments
Open

py.test, assertion for assertDictContainsSubset? #2376

tony opened this issue Apr 21, 2017 · 18 comments
Labels
topic: rewrite related to the assertion rewrite mechanism topic: tracebacks related to displaying and handling of tracebacks type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature

Comments

@tony
Copy link
Member

tony commented Apr 21, 2017

self.assertDictContainsSubset is in unittest in Standard Library.

See https://github.com/python/cpython/blob/bbd3cf8/Lib/unittest/case.py#L1122.

Googled and looked around, I haven't seen a way to do it.

In py.test, assert <dict> in <dict2> should be the same as self.assertDictContainsSubset(dict, dict2.

Is this already a feature and I'm missing it? Is py.test open to a PR for it?

@RonnyPfannschmidt
Copy link
Member

@tony unfortunately that cant work because a in b in python always means b contains a as an element so py.test has no equivalent of that unittest method yet

there is no clear consens on how to add it propperly

@tony
Copy link
Member Author

tony commented Apr 21, 2017

Any ideas come to mind?

Standard library has this functionality, so I'm inclined to say it may be better of it's in pytest. Do you think so? Or would it be better as a plugin?

@smehan
Copy link

smehan commented May 13, 2017

how about creating a new infix operator? Like 'assert dict1 contains dict2'?

@RonnyPfannschmidt
Copy link
Member

@sallner python does not support adding new operators

@tony a plugin may be a good place to experiment with it, py.test is in need for a good way to do more complex assertions than normal python operators allow

however it usually takes a few tries to get it to a good quality level, so a plugin to spearhead that would probably be best

i did create an issue about this in #95 a while back, but i haven't found the time to revisit

@The-Compiler
Copy link
Member

@RonnyPfannschmidt you mean @smehan and not @sallner 😉

@RonnyPfannschmidt
Copy link
Member

oh, yes, thanks for the note

@nicoddemus nicoddemus added topic: rewrite related to the assertion rewrite mechanism topic: tracebacks related to displaying and handling of tracebacks type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature labels Sep 28, 2017
@henri-hulski
Copy link

henri-hulski commented Oct 21, 2017

What's about

assert dict1.items() <= dict2.items()

This should work for Python 3.

Edit:
For Python 2 it would be:

assert dict1.viewitems() <= dict2.viewitems()

@mortonjt
Copy link

mortonjt commented May 7, 2018

This is somewhat tangential, but having a assertIsSubset could be useful, particularly with set objects.

While the below will work just fine

assert set1.issubset(set2)

Actually printing out the elements that are not within set1 (but in set2) would be a nifty/simple feature to have off of the shelf.

@chrisbrake
Copy link

I know this is old, but... you could just do this.

expected = {'important': 'value to test'}
actual = {'important': 'value to test', 'irrelevant': 'potato', 'un-important': 'hair dryer'}
assert expected == {k: v for k, v in actual.items() if k in expected}

@nicoddemus
Copy link
Member

From #6367 (comment):

While @asottile is correct, I do wonder if we should perhaps have an official assertion library/plugin, as it solves practical problems. Immediately comes to mind how often I use fnmatch_lines even though it is private.

But before we introduce assertions, I would like to see a possible list of "assertion helpers" that should be included, so we can take a look at the landscape and decide if this should be indeed a separate library (perhaps under pytest-dev directly) or introduced into pytest itself (I tend for the former, as it doesn't depend on the internals and might have a different release cycle than the core).

This has been brought up before: #2376

cc @ssbarnea

@aklajnert
Copy link
Contributor

Isn't pytest.raises() an example of an assertion built-in to pytest? It doesn't have an assert in the name but acts like it.

@sorXCode
Copy link

@henri-hulski , thanks.
Using self.assertLessEqual(a.items(), b.items()) works fine.

@verhovsky
Copy link
Contributor

The way to do assertDictContainsSubset(dict1, dict2) with pytest is

assert dict2 | dict1 == dict2

on Python 3.9+, or

assert {**dict2, **dict1} == dict2

if you need to support older versions of Python.

@mschneiderwind
Copy link

Can we consider adding something like

assert {'a': 3} <= {'a': 3, 'b': 4}

Today we can do this, but it doesn't feel great.

assert {'a': 3}.items() <= {'a': 3, 'b': 4}.items()

@asottile
Copy link
Member

what you're suggesting requires a change to python itself and is outside of what pytest can do

@soxofaan
Copy link
Contributor

Another approach is the following pattern based on pytest.approx and helpers like https://github.com/utapyngo/pytest-unordered:

class DictSubSet:
    def __init__(self, items: dict):
        self.items = items

    def __eq__(self, other):
        return self.items == {k: other[k] for k in self.items if k in other}

    def __repr__(self):
        return repr(self.items)

assert {"foo": "bar", "meh": 4} == DictSubSet({"foo": "bar"})  # passes

(class name, capitalization and other details are up for dicsussion of course)

As noted above this probably first belongs in a plugin for experimentation. Maybe that already exists somewhere?

@haakenlid
Copy link

I am looking for something like this, too. But it should support nested data structures. It seems most of the suggestions in this issue only does a shallow comparison of dicts.

I found this little used library that almost solves my problem, but doesn't give useful output on failed assertions in pytest.

https://github.com/qweeze/matchlib

@soxofaan
Copy link
Contributor

@haakenlid my basic implementation of DictSubSet does support nesing, e.g.

def test_dict_subset_nesting():
    assert {1: 2, 3: 4, 5: {6: 7, 8: 9}} == DictSubSet({3: 4, 5: DictSubSet({8: 9})})
    assert {1: {2: 3, 4: 5}} == {1: DictSubSet({4: 5})}
    assert {1: {2: {3: {4: 5, 6: 7}}}} == {1: {2: DictSubSet({3: DictSubSet({})})}}

gtempus added a commit to wri/gfw-data-api that referenced this issue Dec 15, 2022
Found a nice way to leverage pytest diffing when asserting a subset of a dict.
pytest-dev/pytest#2376 (comment)
gtempus added a commit to wri/gfw-data-api that referenced this issue Dec 20, 2022
Found a nice way to leverage pytest diffing when asserting a subset of a dict.
pytest-dev/pytest#2376 (comment)
gtempus added a commit to wri/gfw-data-api that referenced this issue Dec 20, 2022
Found a nice way to leverage pytest diffing when asserting a subset of a dict.
pytest-dev/pytest#2376 (comment)
gtempus added a commit to wri/gfw-data-api that referenced this issue Dec 22, 2022
* ✅ test(RasterTileCacheAsset): Add collaborator tests
This commit reflects the effort to assert how `raster_tile_cache_asset`'s collaborators are called. The goal is to use these tests to provide feedback during a refactor of this function. While there was code coverage around the function, there weren't any `assert`s for behavior around its dependencies. Now, we have the feedback for more in-depth structural change.

* ✅ test: Use `call_args_list` with mocks
I definitely think this is a better form than what I was doing previously with `ANY`. The diffs are MUCH nicer.
https://stackoverflow.com/questions/21611559/assert-that-a-method-was-called-with-one-argument-out-of-several/56765027#56765027

* ✅ test: Add RasterTileSetSourceCreationOptions tests
Found a nice way to leverage pytest diffing when asserting a subset of a dict.
pytest-dev/pytest#2376 (comment)

* 🎨 refactor: Move `patch` decorator to class declaration
This removes lots of boilerplate code!
From the python docs:
Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch() finds tests by looking for method names that start with patch.TEST_PREFIX. By default this is 'test', which matches the way unittest finds tests. You can specify an alternative prefix by setting patch.TEST_PREFIX.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: rewrite related to the assertion rewrite mechanism topic: tracebacks related to displaying and handling of tracebacks type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature
Projects
None yet
Development

No branches or pull requests