31

I am writing some unittests in python that are testing if I receive an integer. However sometimes this integer can be off by 1 or 2 and I don't really care. Essentially I want to be able to assert that the received integer is within a certain range, something like:

self.assertBetween(998, 1000, my_integer)

Is there an accepted way of doing this? Or will I have to do something like this:

self.assertTrue(998 <= my_integer)
self.assertTrue(my_integer <= 1000)

EDIT The answers so far suggest:

self.assertTrue(998 <= my_integer <= 1000)

Is there any benefit of this over my example with 2 asserts?

Yep_It's_Me
  • 3,740
  • 4
  • 34
  • 60
  • 1
    Whether you use 1 or 2 asserts depends on what you really want to know and communicate. If you only care that `my_integer` is between the limits, one assert is a cleaner more concise means to test and communicate that to the next guy looking at the code (which maybe you in 3 months). If instead there is a good reason to know if `my_integer` is below 1000 separately to also knowing if `my_integer` is above 998, then it makes more sense to use 2 asserts. – Steven C. Howell Jul 20 '18 at 13:15

4 Answers4

37

You can use a "chained comparison":

self.assertTrue(998 <= my_integer <= 1000)
John Zwinck
  • 207,363
  • 31
  • 261
  • 371
  • 2
    @Yep_It's_Me: you asked if there's any benefit to this over your two separate conditionals. Yes. It's more concise, clearer, and more efficient (one function call instead of two). – John Zwinck Oct 24 '14 at 01:24
  • 8
    @JohnZwinck while I agree this is shorter, I do not think this is something I would recommend because it has the same drawback as two `assertTrue`s. You lose verbosity of FAIL-message. With `assertTrue`, you'll get `AssertionError: False is not true`, which is not very helpful. I prefer to see what's wrong, so I would rather use `assertGreaterEqual(my_integer, 998); assertLessEqual(my_integer, 1000)` - that way you'll know what's wrong immediately (e.g.: `AssertionError: 100 not greater or equal to 998`). – Jan Spurny Nov 27 '17 at 16:02
27

Python has a built in function you may use for this: assertAlmostEqual.

self.assertAlmostEqual(myinteger, 999, delta=1)
# is equivalent to
self.assertTrue(998 <= myinteger <= 1000)
# ... but gives better error messages.

The optional parameter delta specifies the allowed distance from the value you're testing.

Teodor
  • 669
  • 7
  • 14
13

I don't think it's a good idea to use assertTrue with comparison inside - that way you lose any information in FAIL message:

AssertionError: False is not true

Which is not helpful at all and you're basicaly back to "raw" assert and you are losing a lot of unittest's methods benefits.

I would recommend either:

Creating your own custom assert

in which you can print more meaningful message. For example:

import unittest

class BetweenAssertMixin(object):
    def assertBetween(self, x, lo, hi):
        if not (lo <= x <= hi):
            raise AssertionError('%r not between %r and %r' % (x, lo, hi))

class Test1(unittest.TestCase, BetweenAssertMixin):
    def test_between(self):
        self.assertBetween(999, 998, 1000)

    def test_too_low(self):
        self.assertBetween(997, 998, 1000)

    def test_too_high(self):
        self.assertBetween(1001, 998, 1000)

if __name__ == '__main__':
    unittest.main()

then you'll have following output (shortened):

======================================================================
FAIL: test_too_high (__main__.Test1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "example.py", line 19, in test_too_high
    self.assertBetween(1001, 998, 1000)
  File "example.py", line 8, in assertBetween
    raise AssertionError('%r is not between %r and %r' % (x, lo, hi))
AssertionError: 1001 is not between 998 and 1000
======================================================================
FAIL: test_too_low (__main__.Test1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "example.py", line 16, in test_too_low
    self.assertBetween(997, 998, 1000)
  File "example.py", line 8, in assertBetween
    raise AssertionError('%r is not between %r and %r' % (x, lo, hi))
AssertionError: 997 is not between 998 and 1000
----------------------------------------------------------------------

Or use assertLessEqual and assertGreaterEqual

if you don't want custom assert (which does add another traceback record and several lines of code):

...
def test_no_custom_assert(self):
    my_integer = 100
    self.assertGreaterEqual(my_integer, 998)
    self.assertLessEqual(my_integer, 1000)
...

which is a bit longer (it may be shorter in total than adding custom assert if it's used only once) than assertTrue(998 <= my_integer <= 1000) but you'll still get nice fail messages (also without additional traceback record):

======================================================================
FAIL: test_no_custom_assert (__main__.Test1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "example.py", line 23, in test_no_custom_assert
    self.assertGreaterEqual(my_integer, 998)
AssertionError: 100 not greater than or equal to 998
Jan Spurny
  • 4,297
  • 27
  • 43
  • 1
    If you put your custom assertion method in a module with a `_unittest` global the traceback will be omitted, and you'll get the nice clean traceback of using a builtin `TestCase.assertX` method. – DylanYoung Mar 01 '19 at 15:13
1
self.assertTrue(998 <= my_integer <= 1000)
saulspatz
  • 4,309
  • 4
  • 28
  • 38