# fudge.inspector

Value inspectors that can be passed to `fudge.Fake.with_args()` for more expressive argument matching.

As a mnemonic device, an instance of the `fudge.inspector.ValueInspector` is available as “arg” :

    >>> import fudge
    >>> from fudge.inspector import arg
    >>> image = fudge.Fake("image").expects("save").with_args(arg.endswith(".jpg"))

In other words, this declares that the first argument to `image.save()` should end with the suffix “.jpg”

class fudge.inspector.ValueInspector

  Dispatches tests to inspect values.

  any()

    Match any value.

    This is pretty much just a placeholder for when you want to inspect multiple arguments but don’t care about all of them.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> db = fudge.Fake("db")
        >>> db = db.expects("transaction").with_args(
        ...             "insert", isolation_level=arg.any())
        ...
        >>> db.transaction("insert", isolation_level="lock")
        >>> fudge.verify()

    This also passes:

        >>> db.transaction("insert", isolation_level="autocommit")
        >>> fudge.verify()

    The arg_not version will not match anything and is probably not very useful.

        >>> import fudge
        >>> from fudge.inspector import arg_not
        >>> query = fudge.Fake('query').expects_call().with_args(
        ...     arg_not.any()
        ... )
        >>> query('asdf')
        Traceback (most recent call last):
        ...
        AssertionError: fake:query((NOT) arg.any()) was called unexpectedly with args ('asdf')
        >>> query()
        Traceback (most recent call last):
        ...
        AssertionError: fake:query((NOT) arg.any()) was called unexpectedly with args ()

  any_value()

    DEPRECATED: use `arg.any()`

  contains(part)

    Ensure that a value contains some part.

    This is useful for when you only care that a substring or subelement exists in a value.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> addressbook = fudge.Fake().expects("import_").with_args(
        ...                                     arg.contains("Baba Brooks"))
        ...
        >>> addressbook.import_("Bill Brooks; Baba Brooks; Henry Brooks;")
        >>> fudge.verify()

    Since contains() just invokes the __in__() method, checking that a list item is present works as expected :

        >>> colorpicker = fudge.Fake("colorpicker")
        >>> colorpicker = colorpicker.expects("select").with_args(arg.contains("red"))
        >>> colorpicker.select(["green","red","blue"])
        >>> fudge.verify()

    arg_not.contains matches an argument not containing some element.

        >>> from fudge.inspector import arg_not
        >>> colorpicker = colorpicker.expects('select').with_args(arg_not.contains('blue'))
        >>> colorpicker.select('reddish')
        >>> colorpicker.select(['red', 'green'])
        >>> fudge.verify()

        >>> colorpicker.select('blue-green')
        Traceback (most recent call last):
        ...
        AssertionError: fake:colorpicker.select(arg.contains('red'))[0] was called unexpectedly with args ('blue-green')
        >>> colorpicker.select(['red', 'blue', 'green'])
        Traceback (most recent call last):
        ...
        AssertionError: fake:colorpicker.select((NOT) arg.contains('blue'))[1] was called unexpectedly with args (['red', 'blue', 'green'])

  endswith(part)

    Ensure that a value ends with some part.

    This is useful for when values with dynamic parts that are hard to replicate.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> tmpfile = fudge.Fake("tempfile").expects("mkname").with_args(
        ...                                             arg.endswith(".tmp"))
        ...
        >>> tmpfile.mkname("7AakkkLazUUKHKJgh908JKjlkh.tmp")
        >>> fudge.verify()

    The arg_not version works as expected, matching arguments that do not end with the given element.

        >>> from fudge.inspector import arg_not
        >>> query = fudge.Fake('query').expects_call().with_args(arg_not.endswith('Ringo'))
        >>> query('John, Paul, George and Steve')
        >>> fudge.verify()

  has_attr(**attributes)

    Ensure that an object value has at least these attributes.

    This is useful for testing that an object has specific attributes.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> db = fudge.Fake("db").expects("update").with_args(arg.has_attr(
        ...                                                       first_name="Bob",
        ...                                                       last_name="James" ))
        ...
        >>> class User:
        ...     first_name = "Bob"
        ...     last_name = "James"
        ...     job = "jazz musician" # this is ignored
        ...
        >>> db.update(User())
        >>> fudge.verify()

    In case of error, the other object’s __repr__ will be invoked:

        >>> class User:
        ...     first_name = "Bob"
        ...
        ...     def __repr__(self):
        ...         return repr(dict(first_name=self.first_name))
        ...
        >>> db.update(User())
        Traceback (most recent call last):
        ...
        AssertionError: fake:db.update(arg.has_attr(first_name='Bob', last_name='James')) was called unexpectedly with args ({'first_name': 'Bob'})

    When called as a method on arg_not, has_attr does the opposite, and ensures that the argument does not have the specified attributes.

        >>> from fudge.inspector import arg_not
        >>> class User:
        ...     first_name = 'Bob'
        ...     last_name = 'Dobbs'
        >>> query = fudge.Fake('query').expects_call().with_args(
        ...     arg_not.has_attr(first_name='James')
        ... )
        >>> query(User())
        >>> fudge.verify()

  isinstance(cls)

    Check that a value is instance of specified class.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> system = fudge.Fake("system")
        >>> system = system.expects("set_status").with_args(arg.isinstance(str))
        >>> system.set_status("active")
        >>> fudge.verify()

    Should return True if it’s allowed class or False if not.

        >>> system.set_status(31337)
        Traceback (most recent call last):
        ...
        AssertionError: fake:system.set_status(arg.isinstance('str')) was called unexpectedly with args (31337)

  passes_test(test)

    Check that a value passes some test.

    For custom assertions you may need to create your own callable to inspect and verify a value.

        >>> def is_valid(s):
        ...     if s in ('active','deleted'):
        ...         return True
        ...     else:
        ...         return False
        ...
        >>> import fudge
        >>> from fudge.inspector import arg
        >>> system = fudge.Fake("system")
        >>> system = system.expects("set_status").with_args(arg.passes_test(is_valid))
        >>> system.set_status("active")
        >>> fudge.verify()

    The callable you pass takes one argument, the value, and should return True if it’s an acceptable value or False if not.

        >>> system.set_status("sleep")
        Traceback (most recent call last):
        ...
        AssertionError: fake:system.set_status(arg.passes_test(<function is_valid at...)) was called unexpectedly with args ('sleep')

    If it makes more sense to perform assertions in your test function then be sure to return True :

        >>> def is_valid(s):
        ...     assert s in ('active','deleted'), (
        ...         "Unexpected status value: %s" % s)
        ...     return True
        ...
        >>> import fudge
        >>> from fudge.inspector import arg
        >>> system = fudge.Fake("system")
        >>> system = system.expects("set_status").with_args(arg.passes_test(is_valid))
        >>> system.set_status("sleep")
        Traceback (most recent call last):
        ...
        AssertionError: Unexpected status value: sleep

    Using the inverted version, arg_not.passes_test, asserts that the argument does not pass the provided test.

        >>> from fudge.inspector import arg_not
        >>> query = fudge.Fake('query').expects_call().with_args(
        ...     arg_not.passes_test(lambda x: x > 10)
        ... )
        >>> query(5)
        >>> fudge.verify()

  startswith(part)

    Ensure that a value starts with some part.

    This is useful for when values with dynamic parts that are hard to replicate.

        >>> import fudge
        >>> from fudge.inspector import arg
        >>> keychain = fudge.Fake("keychain").expects("accept_key").with_args(
        ...                                                     arg.startswith("_key"))
        ...
        >>> keychain.accept_key("_key-18657yojgaodfty98618652olkj[oollk]")
        >>> fudge.verify()

    Using arg_not.startswith instead ensures that arguments do not start with that part.

        >>> from fudge.inspector import arg_not
        >>> query = fudge.Fake('query').expects_call().with_args(
        ...     arg_not.startswith('asdf')
        ... )
        >>> query('qwerty')
        >>> fudge.verify()

class fudge.inspector.NotValueInspector

  Inherits all the argument methods from ValueInspector, but inverts them to expect the opposite. See the ValueInspector method docstrings for examples.

  __call__(thing)

    This will match any value except the argument given.

        >>> import fudge
        >>> from fudge.inspector import arg, arg_not
        >>> query = fudge.Fake('query').expects_call().with_args(
        ...     arg.any(),
        ...     arg_not('foobar')
        ... )
        >>> query([1, 2, 3], 'asdf')
        >>> query('asdf', 'foobar')
        Traceback (most recent call last):
        ...
        AssertionError: fake:query(arg.any(), arg_not(foobar)) was called unexpectedly with args ('asdf', 'foobar')