Skip to content

Examples

Pretty Failure Messages

Power Assertions (Beta):

Power assertions is in beta! This feature can be turned on using the --power-assertions switch.

Taking inspiration from Groovy, power assertions take the failed assertion statement and break it down so that you can better understand the problem.

from nimoy.specification import Specification

class MySpec(Specification):

    def use_power_assertions(self):
        with setup:
            class SomeClass:
                def __init__(self):
                    self.val = {'d': 'e'}

                def __str__(self):
                    return str(self.val)

            sc = SomeClass()
        with expect:
            sc.val['d'] == 'f'

Running this spec using nimoy --power-assertions true produces:

======================================================================
FAIL: use_power_assertions (builtins.MySpec)
----------------------------------------------------------------------
Traceback (most recent call last):
  [redacted]
AssertionError: Assertion failed:
sc.val['d'] == f
|  |        |
|  e        False
{'d': 'e'}

String Assertions

from nimoy.specification import Specification

class MySpec(Specification):

    def string_assertion(self):
        with given:
            a = "There's a huge difference"
        with expect:
            a == "There's a small difference"

Produces

======================================================================
FAIL: string_assertion (builtins.MySpec)
----------------------------------------------------------------------
Traceback (most recent call last):
  [redacted]
AssertionError: 
Expected: "There's a small difference"
     but: was "There's a huge difference"
Hint:
- There's a huge difference
?           ^^^^

+ There's a small difference
?           ^^^^^

Mock Assertions

from nimoy.specification import Specification
from unittest import mock

class MySpec(Specification):

    def mock_assertion(self):
        with setup:
            the_mock = mock.Mock()
        with when:
            the_mock.some_method()
        with then:
            2 * the_mock.some_method()

Produces

======================================================================
FAIL: mock_assertion (builtins.MySpec)
----------------------------------------------------------------------
Traceback (most recent call last):
  [redacted]
AssertionError: some_method was to be invoked 2 times but was invoked 1

Stimulus and Response Specification

A specification with a setup, when and then block.

when blocks describe a certain action and then blocks assert the results of that action.

from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with setup:
            a = 1

        with when:
            a = a + 1

        with then:
            a == 2

Expecting Exceptions

Use the thrown method to make sure that an expected exception has been thrown.

from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with when:
            raise Exception('Whaaaaat')
        with then:
            err = thrown(Exception)
            str(err[1]) == 'Whaaaaat'

Data-driven Specification

Using the powerful where block, we can parametrise our specification and execute it multiple times with different sets of data.

Data as a Matrix

We can set the parameters in the form of a matrix.

from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with given:
            a = value_of_a
            b = value_of_b

        with expect:
            (a * b) == expected_value

        with where:
            value_of_a | value_of_b | expected_value
            1          | 10         | 10
            2          | 20         | 40

Data as a List

We can also set the parameters in the form of lists.

from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with given:
            a = value_of_a
            b = value_of_b

        with expect:
            (a * b) == expected_value

        with where:
            value_of_a = [1, 2]
            value_of_b = [10, 20]
            expected_value = [10, 40]

Pretty Mock Response Staging

When using unittest mocks you can stage the return values using binary operators.

Use the right shift to always return the same value (return_value):

from unittest import mock
from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with setup:
            the_mock = mock.Mock()

        with when:
            the_mock.some_method() >> 5

        with then:
            the_mock.some_method() == 5
            the_mock.some_method() == 5
            the_mock.some_method() == 5

Or use the left shift operator to return a different value on every invocation (side_effect):

from unittest import mock
from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with setup:
            the_mock = mock.Mock()

        with when:
            the_mock.some_method() << [5, 6, 7]

        with then:
            the_mock.some_method() == 5
            the_mock.some_method() == 6
            the_mock.some_method() == 7

Pretty Mock Assertions

When using unittest Mocks you can write pretty assertions in the then block. Mock assertion expressions are written like a mathematical expression with the format of [NUMBER_OF_INVOCATIONS] * [INVOCATION_TARGET].

[NUMBER_OF_INVOCATIONS] may be a wildcard when filled in with \_.

Invocation target arguments may also be wildcarded by placing _. For example, class.method(_, 3).

from unittest import mock
from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with setup:
            the_mock = mock.Mock()

        with when:
            the_mock.some_method('abcd', True)

        with then:
            1 * the_mock.some_method('abcd', True)

RegEx Matching

Use the @ shorthand for pretty regex matching.

import re

from nimoy.specification import Specification

class MySpec(Specification):

    def my_feature_method(self):
        with expect:
            'The quick brown fox' @ '.+brown.+' # This is valid regex matching!
            'The quick\nbrown fox' @ re.compile('.+brown.+', re.MULTILINE) # You can also provide your own pattern

Skipping Features

You can use unittest's standard decorator to skip features.

import unittest

from nimoy.specification import Specification

class MySpec(Specification):

    @unittest.skip
    def my_feature_method(self):
        with given:
            a = 'The quick brown fox'
        with expect:
            a == 'The quick frown box'