Source

toffee / test_toffee.py

from expecter import expect
from mock import patch, Mock
from toffee import Fixture, Factory, Seq, StormFactory, CallFactory


class Thing(object):
    """
    Object that allows setting of arbitrary properties in the constructor
    """
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)


class TestFixture(object):

    def test_it_creates_an_object(self):

        class MyFixture(Fixture):
            user = Factory(str, 'admin')

        mf = MyFixture()
        mf.setup()
        assert mf.user == 'admin'

    def test_it_resolves_a_lazy_ref(self):

        class MyFixture(Fixture):
            user = Factory(str, 'admin')
            profile = Factory(dict, user=user)

        mf = MyFixture()
        mf.setup()
        assert mf.profile == {'user': 'admin'}

    def test_it_doesnt_allow_duplicate_factories(self):

        factory = Factory(str)
        try:
            Fixture(a=factory, b=factory)
        except ValueError:
            pass
        else:
            assert False, "exception expected"

    def test_it_resolves_an_attribute_on_a_lazy_ref(self):

        class MyFixture(Fixture):
            user = Factory(Thing, id=42)
            profile = Factory(Thing, userid=user.id)

        mf = MyFixture()
        mf.setup()
        assert mf.user.id == 42
        assert mf.profile.userid == 42

    def test_it_resolves_an_anonymous_ref(self):

        class MyFixture(Fixture):
            x = Factory(lambda s: s * 2, s=Factory(str, 'x'))

        mf = MyFixture().setup()
        assert mf.x == 'xx'

    def test_anonymous_factories_are_torn_down(self):

        with patch.object(Factory, '_destroy_object') as destructor:
            class MyFixture(Fixture):
                x = Factory(Thing, s=Factory(str, 'x'))
            mf = MyFixture().setup()
            anonymous_object = mf.x.s
            mf.teardown()

            destructor.assert_any_call(mf, anonymous_object)

    def test_factories_can_be_reused(self):

        templatefactory = Factory(int, x='1')
        assert templatefactory.create_object(Fixture()) == 1
        assert templatefactory(x=2).create_object(Fixture()) == 2

    def test_factories_return_values_are_not_reused_by_default(self):

        user_factory = Factory(Thing)

        class MyFixture(Fixture):
            profile1 = Factory(Thing, user=user_factory)
            profile2 = Factory(Thing, user=user_factory)

        with MyFixture() as f:
            assert f.profile1.user is not f.profile2.user

    def test_factories_return_values_can_be_explicitly_reused(self):

        user_factory = Factory(Thing)

        class MyFixture(Fixture):
            profile1 = Factory(Thing, user=user_factory)
            profile2 = Factory(Thing, user=profile1.user)

        with MyFixture() as f:
            assert f.profile1.user is f.profile2.user

    def test_it_uses_an_arg_generator_factory(self):

        seqfactory = Factory(str, object=Seq())

        class MyFixture(Fixture):
            s1 = seqfactory()
            s2 = seqfactory()

        mf = MyFixture().setup()
        assert mf.s1 == '0'
        assert mf.s2 == '1'

    def test_it_calls_factories_in_order_defined(self):

        seqfactory = Factory(str, object=Seq())

        mf = Fixture(s1=seqfactory(), s2=seqfactory()).setup()
        assert mf.s1 == '0'
        assert mf.s2 == '1'

        mf = Fixture(s2=seqfactory(), s1=seqfactory()).setup()
        assert mf.s2 == '0'
        assert mf.s1 == '1'

    def test_arg_generator_factories_are_fresh_for_each_fixture_setup(self):

        userfactory = Factory(str, Seq('user-%d'))

        class MyFixture(Fixture):
            user1 = userfactory()

        mf = MyFixture()
        assert mf.setup().user1 == 'user-0'
        mf.teardown()
        assert mf.setup().user1 == 'user-0'

    def test_setup_returns_self(self):

        mf = Fixture()
        assert mf is mf.setup()

    def test_cant_nest_setup_calls(self):
        mf = Fixture(u=Factory(int))

        mf.setup()
        try:
            mf.setup()
        except Exception as e:
            assert 'setup() has already been called' in e.args[0]
        else:
            assert False, "Expected exception"

    def test_can_force_nested_setup_calls(self):
        mf = Fixture(u=Factory(int))
        mf.setup()
        try:
            mf.setup(force=True)
        except Exception:
            assert False, "Should not have raised an exception with force=True"

    def test_setup_and_teardown_are_called_by_context_manager(self):
        mf = Fixture(u=Factory(int))
        with mf:
            assert len(mf.created) == 1
        assert len(mf.created) == 0

    def test_it_calls_setup_complete_on_factories(self):

        setup_complete_calls = []

        class CustomFactory(Factory):
            @classmethod
            def setup_complete(*args):
                setup_complete_calls.append(args)

        mf = Fixture(a=CustomFactory(int), b=CustomFactory(int))
        mf.setup()
        expect(setup_complete_calls) == [(CustomFactory, mf, [mf.a, mf.b])]


class TestSeq(object):

    def test_it_returns_a_string_based_sequence(self):
        s = Seq().make_argument_generator()
        assert s() == '0'
        assert s() == '1'

    def test_it_calls_a_function(self):
        s = Seq(lambda n: n * 2).make_argument_generator()
        assert s() == 0
        assert s() == 2

    def test_it_formats_a_string(self):
        s = Seq("item-%d").make_argument_generator()
        assert s() == "item-0"
        assert s() == "item-1"

    def test_it_accepts_a_different_start_value(self):
        s = Seq(start=100).make_argument_generator()
        assert s() == "100"
        assert s() == "101"


class TestFactory(object):

    def test_can_override_configure_object(self):

        def configure_object(ob, context):
            return 'whoa nelly!'

        f = Fixture(s=Factory(str, configure_object=configure_object))
        f.setup()
        assert f.s == 'whoa nelly!'

    def test_can_set_properties_on_sub_factories(self):

        foo_factory = Factory(Thing, name='thing1')
        bar_factory = Factory(Thing, foo=foo_factory)

        with Fixture(bar=bar_factory(foo__name='thing2')) as f:
            expect(f.bar.foo.name) == 'thing2'

    def test_can_set_properties_on_sub_factories_with_setitem(self):

        foo_factory = Factory(Thing, name='thing1')
        bar_factory = Factory(Thing, foo=foo_factory)

        class MyFixture(Fixture):
            bar = bar_factory()
            bar['foo.name'] = 'thing2'

        with MyFixture() as f:
            expect(f.bar.foo.name) == 'thing2'


class TestFactoryOptions(object):

    def test_can_set_factory_in_fixture_class(self):
        class fixture(Fixture):
            factoryoptions = {'foo': 'bar'}
            F = Factory(object)

        with fixture() as f:
            expect(f.factoryoptions) == {'foo': 'bar'}

    def test_can_set_factory_in_fixture_init(self):
        class fixture(Fixture):
            factoryoptions = {'foo': 'bar'}
            F = Factory(object)

        with Fixture(factoryoptions={'foo': 'bar'}) as f:
            expect(f.factoryoptions) == {'foo': 'bar'}

    def test_factoryoptions_are_independent_between_runs(self):

        with Fixture(factoryoptions={'foo': 'bar'}) as f:
            expect(f.factoryoptions) == {'foo': 'bar'}

        with Fixture(factoryoptions={'bar': 'foo'}) as f:
            expect(f.factoryoptions) == {'bar': 'foo'}

    def test_factoryoptions_are_reset_to_class_defaults(self):

        class fixture(Fixture):
            factoryoptions = {'fish': 'chips'}

        with fixture(factoryoptions={'fish': 'fingers'}) as f:
            expect(f.factoryoptions) == {'fish': 'fingers'}

        with fixture() as f:
            expect(f.factoryoptions) == {'fish': 'chips'}


class TestStormFactory(object):

    def setup(self):
        self.store = Mock()
        self.factory = StormFactory.configure(lambda: self.store)

    def test_it_calls_store_add_and_remove(self):
        with Fixture(x=self.factory(int)):
            self.store.add.assert_called_once_with(0)
            assert self.store.remove.called is False
        self.store.remove.assert_called_once_with(0)

    def test_it_doesnt_commit_by_default(self):
        f = Fixture(x=self.factory(int)).setup()
        assert self.store.commit.called is False
        f.teardown()
        assert self.store.commit.called is False

    def test_it_commits_if_requested(self):
        f = Fixture(x=self.factory(int)).setup(commit=True)
        assert self.store.commit.call_count == 1
        f.teardown()
        assert self.store.commit.call_count == 2

    def test_it_flushes_by_default(self):
        f = Fixture(x=self.factory(int)).setup()
        assert self.store.flush.call_count == 1
        f.teardown()
        assert self.store.flush.call_count == 2

    def test_it_doesnt_flushes_if_requested(self):
        f = Fixture(x=self.factory(int)).setup(flush=False)
        assert self.store.flush.called is False
        f.teardown()
        assert self.store.flush.called is False


class TestCallFactory(object):

    def test_it_calls_the_function(self):
        func = Mock()

        with Fixture(x=CallFactory(func)):
            func.assert_called_once_with()