Reference

assert_that()

assert() evaluates a boolean expression, but assert_that() takes an arbitrary object and a matcher, that is applied over the former. This way makes possible to build up complex assertions by means of matcher composition.

CAUTION: Be ware about hamcrest.assert_that()

Note the hamcrest.assert_that() function has two different behavior depending of its arguments:

  • assert_that(actual, matcher, [reason]) In this form the matcher is applied to the actual object. If the matcher fails, it raises AssertionError showing the optional reason.
  • assert_that(value, [reason]) If the boolean interpretation of value is False, it raises AssertionError showing the optional reason.

It implies that something like:

from hamcrest import assert_that

assert_that(foo, bar)

If bar is not a matcher, the assertion is satisfied for any non-false foo, independently of the value of bar. A more obvious example:

from hamcrest import assert_that

assert_that(2 + 2, 5)  # OMG! that assertion IS satisfied!

For this reason, when you need compare values (equivalent to the unit assertEquals) you must always use a matcher, like is_ or equal_to:

assert_that(2 + 2, is_(5))       # that assertion is NOT satisfied!
assert_that(2 + 2, equal_to(5))  # that assertion is NOT satisfied!

Prefer doublex.assert_that

New in version 1.7: - (thanks to Eduardo Ferro)

To avoid the issues described in the previous section, doublex provides an alternative assert_that() implementation that enforces a matcher as second argument.

>>> from doublex import assert_that
>>> assert_that(1, 1)
Traceback (most recent call last):
...
MatcherRequiredError: 1 should be a hamcrest Matcher

Stubbing

The stub provides all methods in the collaborator interface. When the collaborator is not given (a free stub), the stub seems to have any method you invoke on it. The default behavior for non stubbed methods is to return None, although it can be changed (see Changing default stub behavior).

>>> from doublex import Stub
>>> stub = Stub()
>>> stub.method()

This behavior may be customized in each test using the Python context manager facility:

from doublex import Stub

with Stub() as stub:
     stub.method(<args>).returns(<value>)

Hamcrest matchers may be used to define amazing stub conditions:

from hamcrest import all_of, has_length, greater_than, less_than
from doublex import Stub, assert_that, is_

with Stub() as stub:
    stub.foo(has_length(less_than(4))).returns('<4')
    stub.foo(has_length(4)).returns('four')
    stub.foo(has_length(
             all_of(greater_than(4),
                    less_than(8)))).returns('4<x<8')
    stub.foo(has_length(greater_than(8))).returns('>8')

assert_that(stub.foo((1, 2)), is_('<4'))
assert_that(stub.foo('abcd'), is_('four'))
assert_that(stub.foo('abcde'), is_('4<x<8'))
assert_that(stub.foo([0] * 9), is_('>8'))

Stubs returning input

from doublex import Stub, assert_that

def test_returns_input(self):
    with Stub() as stub:
        stub.foo(1).returns_input()

    assert_that(stub.foo(1), is_(1))

Stubs raising exceptions

from doublex import Stub

def test_raises(self):
    with Stub() as stub:
        stub.foo(2).raises(SomeException)

    with self.assertRaises(SomeException):
        stub.foo(2)

Changing default stub behavior

New in version 1.7: - (thanks to Eduardo Ferro)

Any non-stubbed method returns None. But this behavior can be changed by means of set_default_behavior() function. It can be applied to any double class: Stub, Spy, ProxySpy or Mock.

from doublex import Stub, assert_that
from doublex import set_default_behavior, method_returning

set_default_behavior(Stub, method_returning(20))
stub  = Stub()
assert_that(stub.unknown(), is_(20))

Or to a specific instance:

from doublex import Stub, assert_that, is_
from doublex import set_default_behavior, method_returning

stub = Stub()
set_default_behavior(stub, method_returning(20))
assert_that(stub.unknown(), is_(20))

Also, it is possible to raise some exception:

>>> from doublex import Stub, set_default_behavior, method_raising
>>> stub = Stub()
>>> set_default_behavior(stub, method_raising(SomeException))
>>> stub.unknown()
Traceback (most recent call last):
...
SomeException

Asserting method calls

To assert method invocations you need a Spy and the called() matcher.

called()

called() matches method invocation (argument values are not relevant):

from doublex import Spy, assert_that, called

spy = Spy()

spy.m1()
spy.m2(None)
spy.m3("hi", 3.0)
spy.m4([1, 2])

assert_that(spy.m1, called())
assert_that(spy.m2, called())
assert_that(spy.m3, called())
assert_that(spy.m4, called())

with_args(): asserting calling argument values

Match explicit argument values:

from hamcrest import contains_string, less_than, greater_than
from doublex import Spy, assert_that, called

spy = Spy()

spy.m1()
spy.m2(None)
spy.m3(2)
spy.m4("hi", 3.0)
spy.m5([1, 2])
spy.m6(name="john doe")

assert_that(spy.m1, called())
assert_that(spy.m2, called())

assert_that(spy.m1, called().with_args())
assert_that(spy.m2, called().with_args(None))
assert_that(spy.m3, called().with_args(2))
assert_that(spy.m4, called().with_args("hi", 3.0))
assert_that(spy.m5, called().with_args([1, 2]))
assert_that(spy.m6, called().with_args(name="john doe"))

Remember that hamcrest matchers matchers are fully supported:

assert_that(spy.m3, called().with_args(less_than(3)))
assert_that(spy.m3, called().with_args(greater_than(1)))
assert_that(spy.m6, called().with_args(name=contains_string("doe")))

Other example with a string argument and combining several matchers:

from hamcrest import has_length, greater_than, less_than
from doublex import Spy, assert_that, called, never

spy = Spy()

spy.foo("abcd")

assert_that(spy.foo, called().with_args(has_length(4)))
assert_that(spy.foo, called().with_args(has_length(greater_than(3))))
assert_that(spy.foo, called().with_args(has_length(less_than(5))))
assert_that(spy.foo, never(called().with_args(has_length(greater_than(5)))))

anything(): asserting wildcard values

The anything() hamcrest matcher may be used to match any single value. That is useful when only some arguments are relevant:

from hamcrest import anything

spy.foo(1, 2, 20)
spy.bar(1, key=2)

assert_that(spy.foo, called().with_args(1, anything(), 20))
assert_that(spy.bar, called().with_args(1, key=anything()))

ANY_ARG: greedy argument value wildcard

ANY_ARG is a special value that matches any subsequent argument values, including no args. That is, ANY_ANG means “any value for any argument from here”. If anything() is similar to the regular expression ?, ANY_ARG would be equivalent to *.

For this reason, it has no sense to give other values or matchers after an ANY_ARG. It is also applicable to keyword arguments due they have no order. In summary, ANY_ARG:

  • it must be the last positional argument value.
  • it can not be given as keyword value.
  • it can not be given together keyword arguments.

Since version 1.7 a WrongAPI exception is raised if that situations (see issue 9).

An example:

from doublex import ANY_ARG

spy.arg0()
spy.arg1(1)
spy.arg3(1, 2, 3)
spy.arg_karg(1, key1='a')

assert_that(spy.arg0, called())
assert_that(spy.arg0, called().with_args(ANY_ARG))  # equivalent to previous

assert_that(spy.arg1, called())
assert_that(spy.arg1, called().with_args(ANY_ARG))  # equivalent to previous

assert_that(spy.arg3, called().with_args(1, ANY_ARG))
assert_that(spy.arg_karg, called().with_args(1, ANY_ARG))

Also for stubs:

from doublex import Stub, assert_that, ANY_ARG, is_

with Stub() as stub:
    stub.foo(ANY_ARG).returns(True)
    stub.bar(1, ANY_ARG).returns(True)

assert_that(stub.foo(), is_(True))
assert_that(stub.foo(1), is_(True))
assert_that(stub.foo(key1='a'), is_(True))
assert_that(stub.foo(1, 2, 3, key1='a', key2='b'), is_(True))

assert_that(stub.foo(1, 2, 3), is_(True))
assert_that(stub.foo(1, key1='a'), is_(True))

with_some_args(): asserting just relevant arguments

New in version 1.7.

When a method has several arguments and you need to assert an invocation giving a specific value just for some of them, you may use the anything() matcher for the rest of them. That works but the resulting code is a bit dirty:

from hamcrest import anything
from doublex import Spy, assert_that, ANY_ARG

class Foo:
    def five_args_method(self, a, b, c, d, e=None):
        return 4

spy = Spy(Foo)
spy.five_args_method(1, 2, 'bob', 4)

# only the 'c' argument is important in the test
assert_that(spy.five_args_method,
            called().with_args(anything(), anything(), 'bob', anything()))
# assert only 'b' argument
assert_that(spy.five_args_method,
            called().with_args(anything(), 2, ANY_ARG))

The with_some_args() allows to specify just some arguments, assuming all other can take any value. The same example using with_some_arg():

from doublex import Spy, assert_that, called

class Foo:
    def five_args_method(self, a, b, c, d, e=None):
        return 4

spy = Spy(Foo)
spy.five_args_method(1, 2, 'bob', 4)

# only the 'c' argument is important in the test
assert_that(spy.five_args_method,
            called().with_some_args(c='bob'))
# assert only 'b' argument
assert_that(spy.five_args_method,
            called().with_some_args(b=2))

This method may be used with both keyword and non-keyword arguments.

Warning

Formal argument name is mandatory, so this is only applicable to restricted spies (those that are instantiated giving a collaborator).

never()

Convenient replacement for hamcrest.is_not():

from hamcrest import is_not
from doublex import Spy, assert_that, called, never

spy = Spy()

assert_that(spy.m5, is_not(called()))  # is_not() works
assert_that(spy.m5, never(called()))   # but prefer never() due to better error report messages

times(): asserting number of calls

from hamcrest import anything, all_of, greater_than, less_than
from doublex import Spy, assert_that, called, ANY_ARG, never

spy = Spy()

spy.foo()
spy.foo(1)
spy.foo(1)
spy.foo(2)

assert_that(spy.unknown, never(called()))                    # = 0 times
assert_that(spy.foo, called())                               # > 0
assert_that(spy.foo, called().times(greater_than(0)))        # > 0 (same)
assert_that(spy.foo, called().times(4))                      # = 4
assert_that(spy.foo, called().times(greater_than(2)))        # > 2
assert_that(spy.foo, called().times(less_than(6)))           # < 6

assert_that(spy.foo, never(called().with_args(5)))                  # = 0 times
assert_that(spy.foo, called().with_args().times(1))                 # = 1
assert_that(spy.foo, called().with_args(anything()))                # > 0
assert_that(spy.foo, called().with_args(ANY_ARG).times(4))          # = 4
assert_that(spy.foo, called().with_args(1).times(2))                # = 2
assert_that(spy.foo, called().with_args(1).times(greater_than(1)))  # > 1
assert_that(spy.foo, called().with_args(1).times(less_than(5)))     # < 5
assert_that(spy.foo, called().with_args(1).times(
            all_of(greater_than(1), less_than(8))))                 # 1 < times < 8