How can I assert lists equality with pytest
Question:
I’m trying to make some unit tests with pytest.
I was thinking about doing things like that:
actual = b_manager.get_b(complete_set)
assert actual is not None
assert actual.columns == ['bl', 'direction', 'day']
The first assertion in ok but with the second I have an value error.
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I assume it is not the right way to assert the equality of two different lists with pytest.
How can I assert that the dataframe columns (a list) is equal to the expected one?
Thanks
Answers:
You could do a list comprehension to check equality of all values. If you call all
on the list comprehensions result, it will return True
if all parameters are equal.
actual = ['bl', 'direction', 'day']
expected = ['bl', 'direction', 'day']
assert len(actual) == len(expected)
assert all([a == b for a, b in zip(actual, expected)])
print(all([a == b for a, b in zip(actual, expected)]))
>>> True
If you are using the built in unittest.TestCase
, there is already a method that can do that for you: unittest.TestCase.assertListEqual
if you care about the list ordering, and unittest.TestCase.assertCountEqual
if you don’t.
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
See this:
Note:
You can simply use the assert
statement for asserting test
expectations. pytest’s Advanced assertion introspection will
intelligently report intermediate values of the assert expression
freeing you from the need to learn the many names of JUnit legacy
methods.
And this:
Special comparisons are done for a number of cases:
- comparing long strings: a context diff is shown
- comparing long sequences: first failing indices
- comparing dicts: different entries
And the reporting demo:
failure_demo.py:59: AssertionError
_______ TestSpecialisedExplanations.test_eq_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
def test_eq_list(self):
> assert [0, 1, 2] == [0, 1, 3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
See the assertion for lists equality with literal ==
over there? pytest has done the hard work for you.
convert the numpy arrays to python lists and you’ll get a better response than simply using all
or any
. This way, if the test fails you’ll see how the expected vs actual differ
In Python 3.9, this should now work:
def test_arrays_equal():
a = [1, 2, 3]
b = [1, 2, 4]
assert a == b
Or, you can parse the lists to numpy
arrays and use function array_equal
:
import numpy as np
def test_arrays_equal():
a = [1, 2, 3]
b = [1, 2, 4]
ar = np.array(a)
br = np.array(b)
assert np.array_equal(ar, br)
There are two keys to answer this seemingly simple answer:
conditions of equality and assertion error helpfulness.
The conditions of equality depend on given constrains and requirements.
Assertion error should point to the violation of those conditions.
Answer the following questions:
- Is order of lists important?
are [1, 2]
and [2, 1]
equal?
- Can lists have duplicates?
are [1, 2]
and [1, 1, 2]
equal?
- Are there unhashable elements?
are there any mutable types?
- Should assertion error be informative and helpful?
No, No, No, No: symmetric difference between sets
def test_nnnn():
expected = [1, 2, 3]
actual = [4, 3, 2, 1]
difference = set(a) ^ set(b)
assert not difference
E assert not {4, 5}
It is handy to use this method on large lists, because it’s fast and difference
will contain only hm difference between them, so AssertionError
will be compact, however not informative.
No, No, No, Yes: difference between sets with custom message
def test_nnny():
expected = [1, 2, 3, 4]
actual = [5, 3, 2, 1]
lacks = set(expected) - set(actual)
extra = set(actual) - set(expected)
message = f"Lacks elements {lacks} " if lacks else ''
message += f"Extra elements {extra}" if extra else ''
assert not message
E AssertionError: assert not 'Lacks elements {4} Extra elements {5}'
No, Yes, No, Yes: check for duplicates, than difference in sets
Because set()
removes duplicates you should check for them ahead using this answer:
def test_nyny():
expected = [1, 2, 3, 4]
actual = [1, 2, 3, 3, 5]
seen = set()
duplicates = list()
for x in actual:
if x in seen:
duplicates.append(x)
else:
seen.add(x)
lacks = set(expected) - set(actual)
extra = set(actual) - set(expected)
message = f"Lacks elements {lacks} " if lacks else ''
message += f"Extra elements {extra} " if extra else ''
message += f"Duplicate elements {duplicates}" if duplicates else ''
assert not message
E AssertionError: assert not 'Lacks elements {4} Extra elements {5} Duplicate elements [3]'
Yes, Yes, Yes, No: compare lists
def test_yyyn():
expected = [1, 2, 3, 3, 3, {'a': 1}]
actual = [3, 3, 2, 1, {'a': 1}]
assert expected == actual
E AssertionError: assert [1, 2, 3, 3, 3, {'a': 1}] == [3, 3, 2, 1, {'a': 1}]
Yes, Yes, Yes, Yes: extra libraries
Take a look at DeepDiff
You could also use https://github.com/AdityaSavara/UnitTesterSG which can be obtained by pip UnitTesterSG
I’m trying to make some unit tests with pytest.
I was thinking about doing things like that:
actual = b_manager.get_b(complete_set)
assert actual is not None
assert actual.columns == ['bl', 'direction', 'day']
The first assertion in ok but with the second I have an value error.
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I assume it is not the right way to assert the equality of two different lists with pytest.
How can I assert that the dataframe columns (a list) is equal to the expected one?
Thanks
You could do a list comprehension to check equality of all values. If you call all
on the list comprehensions result, it will return True
if all parameters are equal.
actual = ['bl', 'direction', 'day']
expected = ['bl', 'direction', 'day']
assert len(actual) == len(expected)
assert all([a == b for a, b in zip(actual, expected)])
print(all([a == b for a, b in zip(actual, expected)]))
>>> True
If you are using the built in unittest.TestCase
, there is already a method that can do that for you: unittest.TestCase.assertListEqual
if you care about the list ordering, and unittest.TestCase.assertCountEqual
if you don’t.
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
See this:
Note:
You can simply use the
assert
statement for asserting test
expectations. pytest’s Advanced assertion introspection will
intelligently report intermediate values of the assert expression
freeing you from the need to learn the many names of JUnit legacy
methods.
And this:
Special comparisons are done for a number of cases:
- comparing long strings: a context diff is shown
- comparing long sequences: first failing indices
- comparing dicts: different entries
And the reporting demo:
failure_demo.py:59: AssertionError
_______ TestSpecialisedExplanations.test_eq_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
def test_eq_list(self):
> assert [0, 1, 2] == [0, 1, 3]
E assert [0, 1, 2] == [0, 1, 3]
E At index 2 diff: 2 != 3
E Use -v to get the full diff
See the assertion for lists equality with literal ==
over there? pytest has done the hard work for you.
convert the numpy arrays to python lists and you’ll get a better response than simply using all
or any
. This way, if the test fails you’ll see how the expected vs actual differ
In Python 3.9, this should now work:
def test_arrays_equal():
a = [1, 2, 3]
b = [1, 2, 4]
assert a == b
Or, you can parse the lists to numpy
arrays and use function array_equal
:
import numpy as np
def test_arrays_equal():
a = [1, 2, 3]
b = [1, 2, 4]
ar = np.array(a)
br = np.array(b)
assert np.array_equal(ar, br)
There are two keys to answer this seemingly simple answer:
conditions of equality and assertion error helpfulness.
The conditions of equality depend on given constrains and requirements.
Assertion error should point to the violation of those conditions.
Answer the following questions:
- Is order of lists important?
are
[1, 2]
and[2, 1]
equal? - Can lists have duplicates?
are
[1, 2]
and[1, 1, 2]
equal? - Are there unhashable elements?
are there any mutable types?
- Should assertion error be informative and helpful?
No, No, No, No: symmetric difference between sets
def test_nnnn():
expected = [1, 2, 3]
actual = [4, 3, 2, 1]
difference = set(a) ^ set(b)
assert not difference
E assert not {4, 5}
It is handy to use this method on large lists, because it’s fast and difference
will contain only hm difference between them, so AssertionError
will be compact, however not informative.
No, No, No, Yes: difference between sets with custom message
def test_nnny():
expected = [1, 2, 3, 4]
actual = [5, 3, 2, 1]
lacks = set(expected) - set(actual)
extra = set(actual) - set(expected)
message = f"Lacks elements {lacks} " if lacks else ''
message += f"Extra elements {extra}" if extra else ''
assert not message
E AssertionError: assert not 'Lacks elements {4} Extra elements {5}'
No, Yes, No, Yes: check for duplicates, than difference in sets
Because set()
removes duplicates you should check for them ahead using this answer:
def test_nyny():
expected = [1, 2, 3, 4]
actual = [1, 2, 3, 3, 5]
seen = set()
duplicates = list()
for x in actual:
if x in seen:
duplicates.append(x)
else:
seen.add(x)
lacks = set(expected) - set(actual)
extra = set(actual) - set(expected)
message = f"Lacks elements {lacks} " if lacks else ''
message += f"Extra elements {extra} " if extra else ''
message += f"Duplicate elements {duplicates}" if duplicates else ''
assert not message
E AssertionError: assert not 'Lacks elements {4} Extra elements {5} Duplicate elements [3]'
Yes, Yes, Yes, No: compare lists
def test_yyyn():
expected = [1, 2, 3, 3, 3, {'a': 1}]
actual = [3, 3, 2, 1, {'a': 1}]
assert expected == actual
E AssertionError: assert [1, 2, 3, 3, 3, {'a': 1}] == [3, 3, 2, 1, {'a': 1}]
Yes, Yes, Yes, Yes: extra libraries
Take a look at DeepDiff
You could also use https://github.com/AdityaSavara/UnitTesterSG which can be obtained by pip UnitTesterSG