ValidationError assertion not working when pytest shows the same values - python

I'm writing a unit test whereby I want to assert that the error I get:
<ValidationError: "'a list' is not of type 'array'">
is equal too
assert ValidationError(message="'a list' is not of type 'array'")
I'm using a simple assertion:
assert actual == expected
When I run pytest I get the following error:
assert <ValidationError: "'a list' is not of type 'array'"> == <ValidationError: "'a list' is not of type 'array'">
The validation error comes from jsonschema
from jsonschema import ValidationError
Even though they are identical, the assertion still fails. Does anyone know why?

This exception doesn't seem to have __eq__ method defined, so it is being compared using ids - and since those are 2 different objects, they have different ids. Just like this code will throw an error, because Python doesn't really know how 2 different TypeErrors should be compared.
assert TypeError('a') == TypeError('a')
If you check pytest documentation, suggested way to handle expected exceptions looks like this:
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0

Normally I would structure this using pytest.raises
def test_validation_error():
with pytest.raises(ValidationError, match="'a list' is not of type 'array'"):
# whatever you are doing to raise the error
assert ValidationError(message="'a list' is not of type 'array'")

Related

Python equivalent of java junit assertequals

In Java, Junit has some "assert" methods that, when they fail, will tell you something like "Assertion failed. Expected x but saw y".
What's an equivalent library in Python for that, as opposed to "assert x" producing "assertion failed" but not further information?
When you use the assert keyword you can add customised error messages:
assert your_statement_here, "Custom error message here"
Within your custom error message error you can format any variables or data you need to show to debug.
However, if you need something more powerful, as #Asocia said, you will need unittest, specifically assert-methods is what I think you are looking for, and the official documentation can help with that:
https://docs.python.org/3/library/unittest.html#assert-methods
The TestCase class provides several assert methods to check for and
report failures.
assertEqual(a, b) checks: a == b
assertNotEqual(a, b) checks: a != b
... and so on

Mypy rejects type objects created with `type(name, (bases,), {})`

I'm having some trouble getting mypy to accept type objects. I am
convinced I'm just doing it wrong but my google searches have not led me to any answers so far.
class Good(object):
a = 1
def works(thing: Good):
print(thing.a)
o = Good()
works(o)
Bad = type('Bad', (object, ), dict(a=1))
def fails_mypy(thing: Bad):
print(thing.a)
s = Bad()
fails_mypy(s)
Things constructed like 'Good' are ok, while things constructed like 'Bad' fail mypy checks with:
error: Invalid type "test.Bad"
error: Bad? has no attribute "a"
Based on the Unsupported Python Features section of mypys wiki, runtime creation of classes like this isn't currently supported. It cannot understand what Bad is in your function definition. Using reveal_type(Good) and reveal_type(Bad) when executing mypy should make this clear.
An approach to silence these is by using Any. Either using Python 3.6 variable annotation syntax:
Bad: Any = type('Bad', (), {'a':1})
or, with Python < 3.6:
Bad = type('Bad', (), {'a':1}) # type: Any
(in both cases Any should first be imported from typing)
of course, this basically means that your function now accepts anything. A price to pay, but that's what you get with dynamic languages; since Bar is defined at runtime, it can theoretically be anything :-)

Use a custom failure message for `assertRaises()` in Python?

The Python 2.7 unittest docs say:
All the assert methods (except assertRaises(), assertRaisesRegexp()) accept a msg argument that, if specified, is used as the error message on failure
… but what if I want to specify the error message for assertRaises() or assertRaisesRegexp()?
Use case: when testing various values in a loop, if one fails I’d like to know which one:
NON_INTEGERS = [0.21, 1.5, 23.462, math.pi]
class FactorizerTestCase(unittest.TestCase):
def test_exception_raised_for_non_integers(self):
for value in NON_INTEGERS:
with self.assertRaises(ValueError):
factorize(value)
If any of these fails, I get:
AssertionError: ValueError not raised
which isn’t too helpful for me to work out which one failed… if only I could supply a msg= argument like I can with assertEqual() etc!
(I could of course break these out into separate test functions — but maybe there are loads of values I want to test, or it requires some slow/expensive setup, or it’s part of a longer functional test)
I’d love it if I could easily get it to report something like:
AssertionError: ValueError not raised for input 23.462
— but it’s also not a critical enough thing to warrant reimplementing/extending assertRaises() and adding a load more code to my tests.
You could also fallback to using self.fail which feels annoying, but looks a bit less hacky I think
for value in NON_INTEGERS:
with self.assertRaises(ValueError) as cm:
factorize(value)
self.fail('ValueError not raised for {}'.format(value))
1. Easiest (but hacky!) way to do this I’ve found is:
for value in NON_INTEGERS:
with self.assertRaises(ValueError) as cm:
cm.expected.__name__ = 'ValueError for {}'.format(value) # custom failure msg
factorize(value)
which will report this on failure:
AssertionError: ValueError for 23.462 not raised
Note this only works when using the with … syntax.
It works because the assertRaises() context manager does this internally:
exc_name = self.expected.__name__
…
raise self.failureException(
"{0} not raised".format(exc_name))
so could be flaky if the implementation changes, although the Py3 source is similar enough that it should work there too (but can’t say I’ve tried it).
2. Simplest way without relying on implementation is to catch the error and re-raise it with an improved message:
for value in NON_INTEGERS:
try:
with self.assertRaises(ValueError) as cm:
factorize(value)
except AssertionError as e:
raise self.failureException('{} for {}'.format(e.message, value)), sys.exc_info()[2]
The sys.exc_info()[2] bit is to reuse the original stacktrace, but this syntax is Py2 only. This answer explains how to do this for Py3 (and inspired this solution).
But this is already making the test hard to read, so I prefer the first option.
The ‘proper’ solution would require writing a wrapped version of both assertRaises AND the _AssertRaisesContext class, which sounds like overkill when you could just throw in some logging when you get a failure.
I use this instead of assertRaises:
def test_empty_username(self):
# noinspection PyBroadException
try:
my_func(username="")
except Exception:
# If it does, we are OK.
return
# If not, we are here.
self.fail("my_func() must reject empty username.")
In Python 3, unittest now exposes the error message as a proper, publicly accessible property of the _AssertRaisesContext instance you get from with self.assertRaises(). So in Python 3, you can do this:
with self.assertRaises(ValueError) as assertion:
assertion.msg = f"ValueError not raised for input {value}"
factorize(value)

Test for 'ExceptionError' with py.test

Hello I have an class object dice that eventually deletes itself. Using py.test how can I make py.test return positive to the object deleting itself?
I tried this but it is a syntax error:
assert dice raises(ExceptionError)
Thanks.
With pytest, test for expected errors like this
import pytest
with pytest.raises(ZeroDivisionError):
1 / 0
that's an example from the docs, chapter Assertions about expected exceptions
In your case it would be something like this
import pytest
with pytest.raises(ExceptionError)
# Instantiate a dice object
# Do stuff that makes it delete itself
Here I used the error that you yourself named, I don't know if that is the actual error that is raised.
You could check globals(). It contains all global variables.
if "dice" in globals():
do something
else:
raise(Exception)

Python unit testing: make nose show failed assertions values

is it possible to show the assertion values that failed? It shows the traceback and what kind of exception was throw but it would more practical to know which values failed.
Example:
assert result.file == file
AssertionError
You should run nosetests -d this will display the values of the objects that fail the compare in assert.
assert result.file == file, "%s != %s" % (result.file, file,)
That's why ugly self.assert<Foo> methods were introduced in unittest.TestCase instead of nice and short asserts: self.assert<Foo> methods know how to display failure messages.
By the way, I thought that nose do some black magic so in simple cases
assert a == b
should show meaningful error message.
Another possibility: define your own function that does the trick:
def assert_eq(obt, exp):
assert obt==exp, "\n*Expected:\n%s\n*Obtained:\n%s" % (exp, obt)
You can call it instead of assert:
assert_eq ( self.data['SQ'].code, "SQ" )
And this returns this nice error:

Categories