Tests

To run the tests, execute the test.sh script in the root directory.

The majority of my tests are black box tests that ensure given a particular fragment of code, a particular output is achieved. To do this, I created a custom should_dsl matcher that will let me do this:

class Test_Tokenisor_translation(object):
    def setUp(self):
        self.toka = Tokeniser(with_describe_attrs=False)

    def test_it_should_translate_a_describe(self):
        (self.toka, 'describe "Something testable"') |should| result_in('class TestSomethingTestable (object ):pass')

The matcher takes (tokeniser, original) on the left and expects a string on the right. It will use the tokeniser provided to translate the original string, followed by comparing that result to the string on the right.

Note

NoseOfYeti doesn’t have any say on space between tokens and so the output can have some weird spacing.

Below is the specs that noseOfYeti has. To see the original source, just click a spec.

complex_tokeniser_test.py

Test Tokeniser Complex

setUp

    def setUp(self):
        self.toka = Tokeniser()
        self.tokb = Tokeniser(default_kls = 'other')

        ###   SMALL EXAMPLE

        self.small_example = [
        '''
        describe "This":
            before_each:
                self.x = 5
            describe "That":
                before_each:
                    self.y = 6
                describe "Meh":
                    after_each:
                        self.y = None
            describe "Blah":pass
        describe "Another":
            before_each:
                self.z = 8 '''
        ,
        '''
        class TestThis (%(o)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestThis ,self ));self .x =5
        class TestThis_That (TestThis ):
            def setUp (self ):
                noy_sup_setUp (super (TestThis_That ,self ));self .y =6
        class TestThis_That_Meh (TestThis_That ):
            def tearDown (self ):
                noy_sup_tearDown (super (TestThis_That_Meh ,self ));self .y =None
        class TestThis_Blah (TestThis ):pass
        class TestAnother (%(o)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestAnother ,self ));self .z =8

        TestThis .is_noy_spec =True
        TestThis_That .is_noy_spec =True
        TestThis_That_Meh .is_noy_spec =True
        TestThis_Blah .is_noy_spec =True
        TestAnother .is_noy_spec =True
        '''
        ]

        ###   BIG EXAMPLE

        self.big_example = [
        '''
        describe "This":
            before_each:
                self.x = 5
            it 'should':
                if x:
                    pass
                else:
                    x += 9
            describe "That":
                before_each:
                    self.y = 6
                describe "Meh":
                    after_each:
                        self.y = None
                    it "should set __testname__ for non alpha names ' $^":
                        pass
                    it 'should':
                        if y:
                            pass
                        else:
                            pass
                    it 'should have args', arg1, arg2:
                        blah |should| be_good()
            describe "Blah":pass
        ignore "root level $pecial-method*+"
        describe "Another":
            before_each:
                self.z = 8
            it 'should':
                if z:
                    if u:
                        print "hello \
                            there"
                    else:
                        print "no"
                else:
                    pass
        '''
        ,
        '''
        class TestThis (%(o)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestThis ,self ));self .x =5
            def test_should (self ):
                if x :
                    pass
                else :
                    x +=9
        class TestThis_That (TestThis ):
            def setUp (self ):
                noy_sup_setUp (super (TestThis_That ,self ));self .y =6
        class TestThis_That_Meh (TestThis_That ):
            def tearDown (self ):
                noy_sup_tearDown (super (TestThis_That_Meh ,self ));self .y =None
            def test_should_set_testname_for_non_alpha_names (self ):
                pass
            def test_should (self ):
                if y :
                    pass
                else :
                    pass
            def test_should_have_args (self ,arg1 ,arg2 ):
                blah |should |be_good ()
        class TestThis_Blah (TestThis ):pass
        def ignore__root_level_pecial_method ():raise nose.SkipTest
        class TestAnother (%(o)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestAnother ,self ));self .z =8
            def test_should (self ):
                if z :
                    if u :
                        print "hello \
                            there"
                    else :
                        print "no"
                else :
                    pass

        TestThis .is_noy_spec =True
        TestThis_That .is_noy_spec =True
        TestThis_That_Meh .is_noy_spec =True
        TestThis_Blah .is_noy_spec =True
        TestAnother .is_noy_spec =True
        ignore__root_level_pecial_method .__testname__ ="root level $pecial-method*+"
        TestThis_That_Meh .test_should_set_testname_for_non_alpha_names .{func_accessor}__testname__ ="should set __testname__ for non alpha names ' $^"
        '''.format(func_accessor=func_accessor)
        ]

works with tabs

    def test_works_with_tabs(self):
        test, desired = [d.replace('    ', '\t') for d in self.small_example]
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

works with space

    def test_works_with_space(self):
        test, desired = self.small_example
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

keeps good indentation in body with tabs

    def test_keeps_good_indentation_in_body_with_tabs(self):
        test, desired = [d.replace('    ', '\t') for d in self.big_example]
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

keeps good indentation in body with spaces

    def test_keeps_good_indentation_in_body_with_spaces(self):
        test, desired = self.big_example
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

config_test.py

Test ConfigAttributes

config gets attributes from util

    @wraps(method)
    def clear_and_verify(*args, **kw):
        clear_expectations()
        clear_calls()
        try:
            v = method(*args, **kw)
            verify() # if no exceptions
        finally:
            clear_expectations()
        return v

config gets values like attributes

    def test_config_gets_values_like_attributes(self):
        '''Make sure the options we set can be retrieved like values'''
        config = Config()
        config.setup({"a":1, "b":2, "c-d":3})

        config.a |should| be(1)
        config.b |should| be(2)
        config.c_d |should| be(3)

Test ConfigSetup

setup uses util

        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value

it uses extractor if provided

    def test_it_uses_extractor_if_provided(self):
        '''If given an extractor, it applies extractor to the options before setting them'''
        template = fudge.Fake("template")
        def extractor(template, options):
            for key, val in options.items():
                yield key, val + 1

        config = Config(template)
        config.setup({'a':1, 'b':2}, extractor=extractor)
        config._util.values |should| equal_to({'a':2, 'b':3})

it updates values with options

    def test_it_updates_values_with_options(self):
        # With no values to begin with
        config = Config()
        config._util.values |should| equal_to({})
        config.setup({'a':1, 'b':2})
        config._util.values |should| equal_to({'a':1, 'b':2})

it gets values from config file

    def test_it_gets_values_from_config_file(self):
        '''It gets values from a config file'''
        contents = """
            { "a" : 1
            , "b" : 2
            }
        """
        with a_temp_file(contents) as filename:
            config = Config()
            config.setup({"config-file":filename})
            config._util.values |should| equal_to({"config_file":filename, u"a":1, u"b":2})

it overwrites config file with options

    def test_it_overwrites_config_file_with_options(self):
        '''It gets values from a config file'''
        contents = """
            { "a" : 1
            , "b" : 2
            }
        """
        with a_temp_file(contents) as filename:
            config = Config()
            config.setup({"config-file":filename, "a":4})
            config._util.values |should| equal_to({"config_file":filename, "a":4, u"b":2})

Test Default

stringify returns value

    def test_stringify_returns_value(self):
        str(Default("blah")) |should| equal_to("blah")
        if six.PY2:
            unicode(Default("blah")) |should| equal_to("blah")

        str(Default(1)) |should| equal_to("1")
        if six.PY2:
            unicode(Default(1)) |should| equal_to("1")

        class Special(object):
            def __unicode__(self):
                return 'things'
            def __str__(self):
                return 'stuff'
        str(Default(Special())) |should| equal_to("stuff")
        if six.PY2:
            unicode(Default(Special())) |should| equal_to("things")

config_util_test.py

config util initialisation

def test_config_util_initialisation():
    '''Test ConfigUtil initialises properly'''
    template = fudge.Fake("template")
    config_util = ConfigUtil(template)

    config_util.values |should| equal_to({})
    config_util.template |should| be(template)

config util normalising keys

def test_config_util_normalising_keys():
    '''Test ConfigUtil knows how to normalise a key'''
    config_util = ConfigUtil()
    config_util.normalise_key("blah") |should| equal_to("blah")
    config_util.normalise_key("blah_stuff-things") |should| equal_to("blah_stuff_things")
    config_util.normalise_key("blah-stuff-things") |should| equal_to("blah_stuff_things")

Test ConfigUtil ApplyingConfigFile

it sets options from an json file

    def test_it_sets_options_from_an_json_file(self):
        contents = """
            { "a" : "stuff"
            , "b" : "things"
            }
        """
        with a_temp_file(contents) as filename:
            config_util = ConfigUtil()
            config_util.values |should| equal_to({})
            config_util.apply_config_file(filename)
            config_util.values |should| equal_to({"a":"stuff", "b":"things"})

it doesn’t override existing non default values

    def test_it_doesnt_override_existing_non_default_values(self):
        contents = """
            { "a" : "stuff"
            , "b" : "things"
            , "c" : "and"
            , "d" : "shells"
            , "e-f" : "blah"
            , "g-h" : "other"
            }
        """
        with a_temp_file(contents) as filename:
            config_util = ConfigUtil()
            c_val = Default(21)
            config_util.use_options({'b':21, 'c':c_val, 'd':None, "g-h":"meh"})
            config_util.values |should| equal_to({'b':21, 'c':c_val, 'd':None, "g_h":"meh"})

            config_util.apply_config_file(filename)
            config_util.values |should| equal_to({"a":"stuff", "b":21, "c":"and", 'd':None, "e_f":"blah", "g_h":"meh"})

Test ConfigUtil FindingConfigFile

setUp

    def setUp(self):
        self.config_util = ConfigUtil()
        self.fullpath = fudge.Fake('fullpath')
        self.filename = fudge.Fake('filename')

it defaults to noyjson

        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value

it doesn’t care if default doesn’t exist

        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value

it complains if config file doesn’t exist

        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value

it doesn’t care if provided default doesn’t exist

        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value

Test ConfigUtil FindingValue

it returns value as is

    def test_it_returns_value_as_is(self):
        val = fudge.Fake("val")
        config_util = ConfigUtil()
        config_util.values = {"c":val}
        config_util.find_value("c") |should| be(val)

it returns actual value if finds default

    def test_it_returns_actual_value_if_finds_default(self):
        config_util = ConfigUtil()
        config_util.values = {"a":Default(21)}
        config_util.find_value("a") |should| be(21)

it raises attributeError when key doesn’t exist

    def test_it_raises_attributeError_when_key_doesnt_exist(self):
        config_util = ConfigUtil()
        config_util.values = {"a":1}

        config_util.find_value("a") |should| be(1)

        finder = lambda : config_util.find_value("b")
        finder |should| throw(AttributeError, "Config has no value for b")

Test ConfigUtil UsingConfigFile

it doesn’t care if no config file

    @wraps(method)
    def clear_and_verify(*args, **kw):
        clear_expectations()
        clear_calls()
        try:
            v = method(*args, **kw)
            verify() # if no exceptions
        finally:
            clear_expectations()
        return v

it applies config file if one is found

    @wraps(method)
    def clear_and_verify(*args, **kw):
        clear_expectations()
        clear_calls()
        try:
            v = method(*args, **kw)
            verify() # if no exceptions
        finally:
            clear_expectations()
        return v

Test ConfigUtil UsingOptions

it normalises keys

    def test_it_normalises_keys(self):
        self.config_util.use_options({"a-b":1, "c_d":2, "e-f_g-h":3})
        self.config_util.values |should| equal_to({"a_b":1, "c_d":2, "e_f_g_h":3})

it overrides all values

    def test_it_overrides_all_values(self):
        c_val = Default(3)
        self.config_util.use_options({"a":1, "b":2, "c":c_val})
        self.config_util.values |should| equal_to({"a":1, "b":2, "c":c_val})

        self.config_util.use_options({"b":4, "c":5, "d":6})
        self.config_util.values |should| equal_to({"a":1, "b":4, "c":5, "d":6})

it puts options in values

    def test_it_puts_options_in_values(self):
        self.config_util.use_options({"a":1, "b":2})
        self.config_util.values |should| equal_to({"a":1, "b":2})

it works with list of tuples

    def test_it_works_with_list_of_tuples(self):
        c_val = Default(3)
        self.config_util.use_options([("a",1), ("b",2), ("c",c_val)])
        self.config_util.values |should| equal_to({"a":1, "b":2, "c":c_val})

it uses extractor if provided

    def test_it_uses_extractor_if_provided(self):
        template = fudge.Fake("template")
        def extractor(templ, values):
            templ |should| be(template)
            for key, val in values.items():
                yield "e-{}".format(key), val + 1

        self.config_util.template = template
        self.config_util.use_options({"a":1, "b":2, "c":3}, extractor=extractor)
        self.config_util.values |should| equal_to({"e_a":2, "e_b":3, "e_c":4})

it doesn’t complain if extractor returns none

    def test_it_doesnt_complain_if_extractor_returns_none(self):
        '''It's valid for the extractor to say there are no values'''
        template = fudge.Fake("template")
        def extractor(templ, values):
            templ |should| be(template)
            return None

        self.config_util.template = template
        self.config_util.use_options({"a":1, "b":2, "c":3}, extractor=extractor)
        self.config_util.values |should| equal_to({})

general_tokeniser_test.py

Test Tokeniser

no newline in default imports

    def test_no_newline_in_default_imports(self):
        tok = Tokeniser(import_tokens=determine_imports())
        tok.import_tokens |should_not| contain([NEWLINE, '\n'])

is possible to turn off attributes

    def test_is_possible_to_turn_off_attributes(self):
        imports = determine_imports(with_default_imports=False)
        tok = Tokeniser(import_tokens=imports, with_describe_attrs=False)
        (tok, 'describe "Something testable"') |should| result_in('class TestSomethingTestable (object ):pass')

no newline in extended default imports

    def test_no_newline_in_extended_default_imports(self):
        imports = determine_imports(extra_imports='import another.class', with_default_imports=True)
        tok = Tokeniser(import_tokens=imports)
        tok.import_tokens |should_not| contain([NEWLINE, '\n'])
        (tok, '') |should| result_in(
            'import another .class ;import nose ;from nose .tools import *;from noseOfYeti .tokeniser .support import *'
        )

gives describes noy specific attributes

    def test_gives_describes_noy_specific_attributes(self):
        imports = determine_imports(with_default_imports=False)
        tok = Tokeniser(import_tokens = imports)
        (tok, 'describe "Something testable"') |should| result_in(
        '''
        class TestSomethingTestable (object ):pass

        TestSomethingTestable .is_noy_spec =True
        '''
        )

tokeniser has no default imports by default

    def test_tokeniser_has_no_default_imports_by_default(self):
        tok = Tokeniser()
        tok.import_tokens |should| equal_to(None)

determine imports imports nothing by default

    def test_determine_imports_imports_nothing_by_default(self):
        imports = determine_imports()
        tok = Tokeniser(import_tokens=imports)
        (tok, '') |should| result_in('')

is possible to specify extra imports without default imports

    def test_is_possible_to_specify_extra_imports_without_default_imports(self):
        imports = determine_imports(with_default_imports=False, extra_imports="import thing")
        tok = Tokeniser(import_tokens = imports)
        (tok, '') |should| result_in('import thing ')

imports_test.py

Test DetermineImports

extra imports are added

    def test_extra_imports_are_added(self):
        extra_imports = "import thing; import stuff"
        determine_imports(
            extra_imports=extra_imports, with_default_imports=False
        ) |should| equal_to([
            (NAME, 'import'), (NAME, 'thing'), (OP, ';'), (NAME, 'import'), (NAME, 'stuff')
        ])

it returns nothing if no imports

    def test_it_returns_nothing_if_no_imports(self):
        determine_imports(with_default_imports=False) |should| equal_to([])

extra imports added before defaults

    def test_extra_imports_added_before_defaults(self):
        extra_imports = "import thing; import stuff"
        determine_imports(
            extra_imports=extra_imports, with_default_imports=True
        ) |should| equal_to([
              (NAME, 'import'), (NAME, 'thing'), (OP, ';'), (NAME, 'import'), (NAME, 'stuff')
            , (OP, ';') # Extra semicolon inserted
            , (NAME, 'import'), (NAME, 'nose'), (OP, ';')
            , (NAME, 'from'), (NAME, 'nose'), (OP, '.'), (NAME, 'tools'), (NAME, 'import'), (OP, '*'), (OP, ';')
            , (NAME, 'from')
                , (NAME, 'noseOfYeti'), (OP, '.'), (NAME, 'tokeniser'), (OP, '.'), (NAME, "support")
                , (NAME, 'import'), (OP, '*')
        ])

extra imports not added if no defaults

    def test_extra_imports_not_added_if_no_defaults(self):
        determine_imports(with_default_imports=True) |should| equal_to([
              (NAME, 'import'), (NAME, 'nose'), (OP, ';')
            , (NAME, 'from'), (NAME, 'nose'), (OP, '.'), (NAME, 'tools'), (NAME, 'import'), (OP, '*'), (OP, ';')
            , (NAME, 'from')
                , (NAME, 'noseOfYeti'), (OP, '.'), (NAME, 'tokeniser'), (OP, '.'), (NAME, "support")
                , (NAME, 'import'), (OP, '*')
        ])

nesting_tokeniser_test.py

Test Tokeniser Nesting

setUp

    def setUp(self):
        self.toka = Tokeniser(with_describe_attrs=False)
        self.tokb = Tokeniser(with_describe_attrs=False, default_kls = 'other')

        ###   SMALL EXAMPLE (WITHOUT PASS)

        self.small_example = [
        '''
        describe "This":
            describe "That":

                describe "Meh":pass
            context "Blah":pass
        describe "Another":pass '''
        ,
        '''
        class TestThis (%(o)s ):pass
        class TestThis_That (TestThis ):pass
        class TestThis_That_Meh (TestThis_That ):pass
        class TestThis_Blah (TestThis ):pass
        class TestAnother (%(o)s ):pass
        '''
        ]

        ###   SMALL EXAMPLE (WITH PATH FOR BW COMPAT)

        self.small_example_with_pass = [
        '''
        context "This":pass
            describe "That":pass
                describe "Meh":pass
            describe "Blah":pass
        describe "Another":pass '''
        ,
        '''
        class TestThis (%(o)s ):pass
        class TestThis_That (TestThis ):pass
        class TestThis_That_Meh (TestThis_That ):pass
        class TestThis_Blah (TestThis ):pass
        class TestAnother (%(o)s ):pass
        '''
        ]

        ###   BIG EXAMPLE

        self.big_example = [
        '''
        describe "This":
            it 'should':
                if x:
                    pass
                else:
                    x += 9
            describe "That":
                describe "Meh":
                    it 'should':
                        if y:
                            pass
                        else:
                            pass
            describe "Blah":pass
        describe "Another":
            it 'should':
                if z:
                    if u:
                        print "hello \
                            there"
                    else:
                        print "no"
                else:
                    pass
        '''
        ,
        '''
        class TestThis (%(o)s ):
            def test_should (self ):
                if x :
                    pass
                else :
                    x +=9
        class TestThis_That (TestThis ):pass
        class TestThis_That_Meh (TestThis_That ):
            def test_should (self ):
                if y :
                    pass
                else :
                    pass
        class TestThis_Blah (TestThis ):pass
        class TestAnother (%(o)s ):
            def test_should (self ):
                if z :
                    if u :
                        print "hello \
                            there"
                    else :
                        print "no"
                else :
                    pass
        '''
        ]

works with tabs

    def test_works_with_tabs(self):
        test, desired = [d.replace('    ', '\t') for d in self.small_example]
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

works with space

    def test_works_with_space(self):
        test, desired = self.small_example
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

works with tabs and inline pass

    def test_works_with_tabs_and_inline_pass(self):
        test, desired = [d.replace('    ', '\t') for d in self.small_example_with_pass]
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

works with space and inline pass

    def test_works_with_space_and_inline_pass(self):
        test, desired = self.small_example_with_pass
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

keeps good indentation in body with tabs

    def test_keeps_good_indentation_in_body_with_tabs(self):
        test, desired = [d.replace('    ', '\t') for d in self.big_example]
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

keeps good indentation in body with spaces

    def test_keeps_good_indentation_in_body_with_spaces(self):
        test, desired = self.big_example
        (self.toka, test) |should| result_in(desired % {'o' : 'object'})
        (self.tokb, test) |should| result_in(desired % {'o' : 'other'})

names nested describes with part of parents name

    def test_names_nested_describes_with_part_of_parents_name(self):
        test = 'describe "a":\n\tdescribe "b":'
        desired = 'class TestA (object ):pass\nclass TestA_B (TestA ):'
        (self.toka, test) |should| result_in(desired)

spec_codec_test.py

Test RegisteringCodec

not registering codec leads to error

    def test_not_registering_codec_leads_to_error(self):
        with a_temp_file(example_specd_tests) as filename:
            process = subprocess.Popen([sys.executable, filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
            process.wait()
            process.returncode |should| be(1)

            expected_output = 'File "{}", line 1\nSyntaxError: encoding problem'.format(filename)
            process.stdout.read().decode('utf8').strip() |should| start_with(expected_output)

registering codec doesn’t lead to error

    def test_registering_codec_doesnt_lead_to_error(self):
        with a_temp_dir() as tempdir:
            with open(os.path.join(tempdir, "codez.py"), 'w') as fle:
                fle.write(example_specd_tests)

            with open(os.path.join(tempdir, "spec_setup.py"), 'w') as fle:
                fle.write(init_codez)

            filename = os.path.join(tempdir, "script.py")
            with open(filename, 'w') as fle:
                fle.write("import spec_setup; import codez")

            process = subprocess.Popen([sys.executable, filename], cwd=tempdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
            process.wait()
            if process.returncode != 0:
                print(process.stdout.read().decode('utf8'))
            process.returncode |should| be(0)

            expected_output = 'test_should_totally_work'
            process.stdout.read().decode('utf8').strip() |should| equal_to(expected_output)

support_test.py

TestWrapWorks

it ensures setUp from super classes get called

    def test_it_ensures_setUp_from_super_classes_get_called(self):
        three = Three()
        hasattr(three, "blah") |should| be(False)
        three.setUp()
        three.blah |should| equal_to([1, 2, 3])

it ensures tearDown from super classes get called

    def test_it_ensures_tearDown_from_super_classes_get_called(self):
        three = Three()
        hasattr(three, "meh") |should| be(False)
        three.tearDown()
        three.meh |should| equal_to([4, 5, 6])

test_chooser_test.py

Test TestChooser

it resets done when told about new module

    def test_it_resets_done_when_told_about_new_module(self):
        self.test_chooser.done |should| equal_to({})
        self.test_chooser.done['a'] = 3
        self.test_chooser.done |should| equal_to({'a':3})
        self.test_chooser.new_module()
        self.test_chooser.done |should| equal_to({})

already visited puts kls name key in done or returns True

    def test_already_visited_puts_kls_name_key_in_done_or_returns_True(self):
        self.test_chooser.done |should| equal_to({})
        self.test_chooser.already_visited('a', 'b') |should| be(False)
        self.test_chooser.done |should| equal_to({'a.b' : True})
        self.test_chooser.already_visited('a', 'b') |should| be(True)

        self.test_chooser.already_visited('c', 'd') |should| be(False)
        self.test_chooser.done |should| equal_to({'a.b' : True, 'c.d' : True})
        self.test_chooser.already_visited('c', 'd') |should| be(True)

.consider() method

it ignores functions already visited

    def test_it_ignores_functions_already_visited(self):
        self.TestKlsWithInherited.is_noy_spec = True
        self.test_chooser.consider(self.TestKlsWithInherited().test_on_subclass) |should| be(True)
        self.test_chooser.consider(self.TestKlsWithInherited().test_on_subclass) |should| be(False)

it ignores if method starts with ignore

    def test_it_ignores_if_method_starts_with_ignore(self):
        self.test_chooser.consider(self.TestKlsForTest().ignore__test) |should| be(False)

it ignores if method has kls in ignoreKls

    def test_it_ignores_if_method_has_kls_in_ignoreKls(self):
        self.TestIgnoredKls.is_noy_spec = True
        self.test_chooser.consider(self.TestIgnoredKls().test_things) |should| be(True)
        self.test_chooser.consider(self.TestIgnoredKls().test_things, ignore_kls=['TestIgnoredKls']) |should| be(False)

it ignores if method has test set to false

    def test_it_ignores_if_method_has__test__set_to_false(self):
        self.test_chooser.consider(self.TestKlsForTest().test_with__test__set) |should| be(False)

it returns None if kls does not have is noy test set

    def test_it_returns_None_if_kls_does_not_have_is_noy_test_set(self):
        self.test_chooser.consider(self.TestKlsForTest().test_actual) |should| be(None)

it ignores inherited tests if is noy test is set on kls

    def test_it_ignores_inherited_tests_if_is_noy_test_is_set_on_kls(self):
        self.test_chooser.consider(self.TestKlsWithInherited().test_actual) |should| be(None)
        self.TestKlsWithInherited.is_noy_spec = True
        self.test_chooser.consider(self.TestKlsWithInherited().test_actual) |should| be(False)
        self.test_chooser.consider(self.TestKlsWithInherited().test_on_subclass) |should| be(True)

it ignores parent if specified to only run tests in children

    def test_it_ignores_parent_if_specified_to_only_run_tests_in_children(self):
        self.TestKlsParent.is_noy_spec = True
        self.test_chooser.consider(self.TestKlsParent().test_one) |should| be(False)
        self.test_chooser.consider(self.TestKlsParent().test_two) |should| be(False)

it runs parent tests in child if specified in parent to only run tests in children

    def test_it_runs_parent_tests_in_child_if_specified_in_parent_to_only_run_tests_in_children(self):
        self.TestKlsParent.is_noy_spec = True
        self.TestKlsChild.is_noy_spec = True
        self.test_chooser.consider(self.TestKlsChild().test_one) |should| be(True)
        self.test_chooser.consider(self.TestKlsChild().test_two) |should| be(True)

it doesn’t run grandparent tests if specified in grandparent to only run tests in children

    def test_it_doesnt_run_grandparent_tests_if_specified_in_grandparent_to_only_run_tests_in_children(self):
        self.TestKlsParent.is_noy_spec = True
        self.TestKlsChild.is_noy_spec = True
        self.TestKlsGrandChild.is_noy_spec = True
        self.test_chooser.consider(self.TestKlsGrandChild().test_one) |should| be(False)
        self.test_chooser.consider(self.TestKlsGrandChild().test_two) |should| be(False)

translation_tokeniser_test.py

Test Tokenisor translation

setUp

    def setUp(self):
        self.toka = Tokeniser(with_describe_attrs=False)
        self.tokb = Tokeniser(with_describe_attrs=False, default_kls = 'other')

translates an it

    def test_translates_an_it(self):
        (self.toka, 'it "should do this thing":') |should| result_in('def test_should_do_this_thing ():')
        (self.tokb, 'it "should do this thing":') |should| result_in('def test_should_do_this_thing ():')

        # Same tests, but with newlines in front
        (self.toka, '\nit "should do this thing":') |should| result_in('\ndef test_should_do_this_thing ():')
        (self.tokb, '\nit "should do this thing":') |should| result_in('\ndef test_should_do_this_thing ():')

has ignorable its

    def test_has_ignorable_its(self):
        (self.toka, '\nignore "should be ignored"') |should| result_in(
            '\ndef ignore__should_be_ignored ():raise nose.SkipTest '
            )
        (self.toka, '\nignore "should be ignored"') |should| result_in(
            '\ndef ignore__should_be_ignored ():raise nose.SkipTest '
            )

translates a describe

    def test_translates_a_describe(self):
        (self.toka, 'describe "Something testable"') |should| result_in('class TestSomethingTestable (object ):pass')
        (self.tokb, 'describe "Something testable"') |should| result_in('class TestSomethingTestable (other ):pass')

        # Same tests, but with newlines in front
        (self.toka, '\ndescribe "Something testable"') |should| result_in('\nclass TestSomethingTestable (object ):pass')
        (self.tokb, '\ndescribe "Something testable"') |should| result_in('\nclass TestSomethingTestable (other ):pass')

turns before each into setup

    def test_turns_before_each_into_setup(self):
        (self.toka, 'before_each:') |should| result_in('def setUp (self ):')

        # Same tests, but with newlines in front
        (self.toka, '\nbefore_each:') |should| result_in('\ndef setUp (self ):')

no transform inside expression

    def test_no_transform_inside_expression(self):
        (self.toka, 'variable = before_each') |should| result_in('variable =before_each ')
        (self.toka, 'variable = after_each')  |should| result_in('variable =after_each ')
        (self.toka, 'variable = describe')    |should| result_in('variable =describe ')
        (self.toka, 'variable = ignore')      |should| result_in('variable =ignore ')
        (self.toka, 'variable = it')          |should| result_in('variable =it ')

        # Same tests, but with newlines in front
        (self.toka, '\nvariable = before_each') |should| result_in('\nvariable =before_each ')
        (self.toka, '\nvariable = after_each')  |should| result_in('\nvariable =after_each ')
        (self.toka, '\nvariable = describe')    |should| result_in('\nvariable =describe ')
        (self.toka, '\nvariable = ignore')      |should| result_in('\nvariable =ignore ')
        (self.toka, '\nvariable = it')          |should| result_in('\nvariable =it ')

turns after each into teardown

    def test_turns_after_each_into_teardown(self):
        (self.toka, 'after_each:') |should| result_in('def tearDown (self ):')

        # Same tests, but with newlines in front
        (self.toka, '\nafter_each:') |should| result_in('\ndef tearDown (self ):')

it allows default arguments for its

    def test_it_allows_default_arguments_for_its(self):
        test = '''
        it "is a test with default arguments", thing=2, other=[3]

        describe "group":
            it "has self and default args", blah=None, you=(3, 4,
                5, 5):
                # Test space is respected

                1 |should| be(2)
        '''

        desired = '''
        def test_is_a_test_with_default_arguments (thing =2 ,other =[3 ]):raise nose.SkipTest

        class TestGroup (%(dflt)s ):
            def test_has_self_and_default_args (self ,blah =None ,you =(3 ,4 ,
            5 ,5 )):
            # Test space is respected

                1 |should |be (2 )
        '''

        (self.toka, test) |should| result_in(desired % {'dflt': "object"})
        (self.tokb, test) |should| result_in(desired % {'dflt': "other"})

turns an it without colon into skippable

    def test_turns_an_it_without_colon_into_skippable(self):
        (self.toka, 'it "should be skipped"\n') |should| result_in(
            'def test_should_be_skipped ():raise nose.SkipTest '
        )

        (self.toka, 'it "should not be skipped":\n') |should| result_in(
            'def test_should_not_be_skipped ():'
        )

        # Same tests, but with newlines in front
        (self.toka, '\nit "should be skipped"\n') |should| result_in(
            '\ndef test_should_be_skipped ():raise nose.SkipTest '
        )

        (self.toka, '\nit "should not be skipped":\n') |should| result_in(
            '\ndef test_should_not_be_skipped ():'
        )

gives setups super call when in describes

    def test_gives_setups_super_call_when_in_describes(self):
        test = '''
        describe "Thing":
            before_each:
                self.x = 5
        '''

        desired = '''
        class TestThing (object ):
            def setUp (self ):
                noy_sup_setUp (super (TestThing ,self ));self .x =5
        '''

        (self.toka, test) |should| result_in(desired)
        # and with tabs
        (self.toka, test.replace('    ', '\t')) |should| result_in(desired.replace('    ', '\t'))

gives teardowns super call when in describes

    def test_gives_teardowns_super_call_when_in_describes(self):
        test = '''
        describe "Thing":
            after_each:
                self.x = 5
        '''

        desired = '''
        class TestThing (object ):
            def tearDown (self ):
                noy_sup_tearDown (super (TestThing ,self ));self .x =5
        '''

        (self.toka, test) |should| result_in(desired)
        # and with tabs
        (self.toka, test.replace('    ', '\t')) |should| result_in(desired.replace('    ', '\t'))

adds arguments to its if declared on same line

    def test_adds_arguments_to_its_if_declared_on_same_line(self):
        (self.toka, 'it "should do this thing", blah, meh:') |should| result_in(
            'def test_should_do_this_thing (blah ,meh ):'
            )
        (self.tokb, 'it "should do this thing", blah, meh:') |should| result_in(
            'def test_should_do_this_thing (blah ,meh ):'
            )

        # Same tests, but with newlines in front
        (self.toka, '\nit "should do this thing", blah, meh:') |should| result_in(
            '\ndef test_should_do_this_thing (blah ,meh ):'
            )
        (self.tokb, '\nit "should do this thing", blah, meh:') |should| result_in(
            '\ndef test_should_do_this_thing (blah ,meh ):'
            )

indentation should work for inline python code

    def test_indentation_should_work_for_inline_python_code(self):
        test = '''
        describe 'this':
            describe 'that':
                pass

        class SomeMockObject(object):
            def indented_method()
        '''

        desired = '''
        class TestThis (object ):pass
        class TestThis_That (TestThis ):
            pass

        class SomeMockObject (object ):
            def indented_method ()
        '''

        (self.toka, test) | should | result_in(desired)

sets testname on non alphanumeric test names

    def test_sets__testname__on_non_alphanumeric_test_names(self):
        test = '''
        it "(root level) should work {well}"
            3 |should| be(4)
        describe "SomeTests":
            it "doesn't get phased by $special characters"

            describe "NestedDescribe":
                it "asdf $% asdf":
                    1 |should| be(2)
        it "(root level) should also [work]"
        '''

        desired = '''
        def test_root_level_should_work_well ():raise nose.SkipTest
            3 |should |be (4 )
        class TestSomeTests (%s ):
            def test_doesnt_get_phased_by_special_characters (self ):raise nose.SkipTest

        class TestSomeTests_NestedDescribe (TestSomeTests ):
            def test_asdf_asdf (self ):
                1 |should |be (2 )
        def test_root_level_should_also_work ():raise nose.SkipTest
        test_root_level_should_work_well .__testname__ ="(root level) should work {{well}}"
        test_root_level_should_also_work .__testname__ ="(root level) should also [work]"
        TestSomeTests .test_doesnt_get_phased_by_special_characters .{func_accessor}__testname__ ="doesn't get phased by $special characters"
        TestSomeTests_NestedDescribe .test_asdf_asdf .{func_accessor}__testname__ ="asdf $%% asdf"
        '''.format(func_accessor=func_accessor)

        (self.toka, test) |should| result_in(desired % "object")
        (self.tokb, test) |should| result_in(desired % "other")

can properly dedent after block of just containers

    def test_can_properly_dedent_after_block_of_just_containers(self):
        test = '''
        it "should ensure askers are None or boolean or string":
            for val in (None, False, 'asdf', u'asdf', lambda: 1):
                (lambda : Step(askBeforeAction  = val)) |should_not| throw(Problem)
                (lambda : Step(askDesiredResult = val)) |should_not| throw(Problem)
                (lambda : Step(blockBeforeGet   = val)) |should_not| throw(Problem)

            for val in (1, True):
                (lambda : Step(askBeforeAction  = val)) |should| throw(Problem)
                (lambda : Step(askDesiredResult = val)) |should| throw(Problem)
                (lambda : Step(blockBeforeGet   = val)) |should| throw(Problem)

            3 |should| be(3)
        '''

        desired = '''
        def test_should_ensure_askers_are_None_or_boolean_or_string ():
            for val in (None ,False ,'asdf',u'asdf',lambda :1 ):
                (lambda :Step (askBeforeAction =val ))|should_not |throw (Problem )
                (lambda :Step (askDesiredResult =val ))|should_not |throw (Problem )
                (lambda :Step (blockBeforeGet =val ))|should_not |throw (Problem )

            for val in (1 ,True ):
                (lambda :Step (askBeforeAction =val ))|should |throw (Problem )
                (lambda :Step (askDesiredResult =val ))|should |throw (Problem )
                (lambda :Step (blockBeforeGet =val ))|should |throw (Problem )

            3 |should |be (3 )
        '''

        (self.toka, test) |should| result_in(desired)
        (self.tokb, test) |should| result_in(desired)

indentation should work regardless of crazy groups

    def test_indentation_should_work_regardless_of_crazy_groups(self):
        test = """
        describe 'a':
            it 'asdf':
                l = [ True
                    , False
                        , 1
                , 2
                ]

                t = (1
                        , 2
                , 3
            , 4
                    , 5,
                )

                d = {'asdf' : True}

                t2 = (True
                ,    False
        )
            it 'asdf2'"""

        desired = """
        class TestA (object ):
            def test_asdf (self ):
                l =[True
                ,False
                ,1
                ,2
                ]

                t =(1
                ,2
                ,3
                ,4
                ,5 ,
                )

                d ={'asdf':True }

                t2 =(True
                ,False
                )
            def test_asdf2 (self ):raise nose.SkipTest
        """

        (self.toka, test) |should| result_in(desired)

indentation for test should work after skipped

    def test_indentation_for_test_should_work_after_skipped_test(self):
        test = """
        describe 'thing':
            it 'should be skipped'
            it 'shouldnt be skipped':
                print 'hi'

            it 'another that should be skipped'

            it 'another that shouldnt be skipped':
                print 'hi'"""

        desired = """
        class TestThing (object ):
            def test_should_be_skipped (self ):raise nose.SkipTest
            def test_shouldnt_be_skipped (self ):
                print 'hi'

            def test_another_that_should_be_skipped (self ):raise nose.SkipTest

            def test_another_that_shouldnt_be_skipped (self ):
                print 'hi'
        """

        (self.toka, test) |should| result_in(desired)

it maintains line numbers when pass on another line

    def test_it_maintains_line_numbers_when_pass_on_another_line(self):
        test = '''
        it "is a function with a pass": pass

        it "is a function with a pass on another line":
            pass

        it "is a function with a pass on another line further below":
            #comment or something


            pass

        describe "block with a pass":
            pass

        describe "block with comment and pass":
            # comment or something
            pass

        describe "Nesting and passes": pass
            # comment
            describe "Nested":
                pass

                describe "More Nesting":
                    # comment


                    pass
        '''

        desired = '''
        def test_is_a_function_with_a_pass ():pass

        def test_is_a_function_with_a_pass_on_another_line ():
            pass

        def test_is_a_function_with_a_pass_on_another_line_further_below ():
        #comment or something


            pass

        class TestBlockWithAPass (%(dflt)s ):
            pass

        class TestBlockWithCommentAndPass (%(dflt)s ):
        # comment or something
            pass

        class TestNestingAndPasses (%(dflt)s ):pass
        # comment
        class TestNestingAndPasses_Nested (TestNestingAndPasses ):
            pass

        class TestNestingAndPasses_Nested_MoreNesting (TestNestingAndPasses_Nested ):
        # comment


            pass
        '''

        (self.toka, test) |should| result_in(desired % {'dflt': "object"})
        (self.tokb, test) |should| result_in(desired % {'dflt': "other"})

no added arguments to its if not declared on same line

    def test_no_added_arguments_to_its_if_not_declared_on_same_line(self):
        (self.toka, 'it "should do this thing"\n, blah, meh') |should| result_in(
            "def test_should_do_this_thing ():raise nose.SkipTest\n,blah ,meh "
            )
        (self.tokb, 'it "should do this thing"\n, blah, meh') |should| result_in(
            "def test_should_do_this_thing ():raise nose.SkipTest\n,blah ,meh "
            )

        # Same tests, but with newlines in front
        (self.toka, '\nit "should do this thing"\n, blah, meh') |should| result_in(
            "\ndef test_should_do_this_thing ():raise nose.SkipTest\n,blah ,meh "
            )
        (self.tokb, '\nit "should do this thing"\n, blah, meh') |should| result_in(
            "\ndef test_should_do_this_thing ():raise nose.SkipTest\n,blah ,meh "
            )

indentation for describe should work after skipped

    def test_indentation_for_describe_should_work_after_skipped_test(self):
        test = '''
        describe 'thing':
            it 'should be skipped'
            describe 'that':
                pass'''

        desired = '''
        class TestThing (object ):
            def test_should_be_skipped (self ):raise nose.SkipTest
        class TestThing_That (TestThing ):
            pass
        '''
        (self.toka, test) |should| result_in(desired)

it doesn’t add semicolon after noy setup if not necessary

    def test_it_doesnt_add_semicolon_after_noy_setup_if_not_necessary(self):
        test = '''
            describe "block with necessary semicolon":
                before_each:
                    two = 1 + 1

            describe "block with unecessary semiclon":
                before_each:
                    #comment
                    pass

                after_each:

                    pass
        '''

        desired = '''
        class TestBlockWithNecessarySemicolon (%(dflt)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestBlockWithNecessarySemicolon ,self ));two =1 +1

        class TestBlockWithUnecessarySemiclon (%(dflt)s ):
            def setUp (self ):
                noy_sup_setUp (super (TestBlockWithUnecessarySemiclon ,self ))#comment
                pass

            def tearDown (self ):
                noy_sup_tearDown (super (TestBlockWithUnecessarySemiclon ,self ))
                pass
        '''
        (self.toka, test) |should| result_in(desired % {'dflt': "object"})
        (self.tokb, test) |should| result_in(desired % {'dflt': "other"})

it keeps comments placed after setup and teardown methods

    def test_it_keeps_comments_placed_after_setup_and_teardown_methods(self):
        test = '''
            describe "Kls":
                before_each: # Comment one

                    pass

                after_each: # Comment two

                    pass

            describe "Kls2":
                before_each: # Comment three
                    two = 1 + 1

                after_each: # Comment four
                    #comment
                    pass
        '''

        desired = '''
        class TestKls (%(dflt)s ):
            def setUp (self ):# Comment one
                noy_sup_setUp (super (TestKls ,self ))
                pass

            def tearDown (self ):# Comment two
                noy_sup_tearDown (super (TestKls ,self ))
                pass

        class TestKls2 (%(dflt)s ):
            def setUp (self ):# Comment three
                noy_sup_setUp (super (TestKls2 ,self ));two =1 +1

            def tearDown (self ):# Comment four
                noy_sup_tearDown (super (TestKls2 ,self ))#comment
                pass
        '''
        (self.toka, test) |should| result_in(desired % {'dflt': "object"})
        (self.tokb, test) |should| result_in(desired % {'dflt': "other"})

allows definition of different base class for next describe

    def test_allows_definition_of_different_base_class_for_next_describe(self):
        test = '''
        describe unittest.TestCase "This thing":pass
        describe "Another thing":pass
        '''

        desired = '''%s
        class TestThisThing (unittest .TestCase ):pass
        class TestAnotherThing (%s ):pass
        '''

        (self.toka, test) |should| result_in(desired % ('', 'object'))
        (self.tokb, test) |should| result_in(desired % ('', 'other'))

        # Same tests, but with newlines in front
        (self.toka, '\n%s' % test) |should| result_in(desired % ('\n', 'object'))
        (self.tokb, '\n%s' % test) |should| result_in(desired % ('\n', 'other'))

adds arguments to its if declared on same line and work with skipTest

    def test_adds_arguments_to_its_if_declared_on_same_line_and_work_with_skipTest(self):
        (self.toka, 'it "should do this thing", blah, meh') |should| result_in(
            'def test_should_do_this_thing (blah ,meh ):raise nose.SkipTest '
            )
        (self.tokb, 'it "should do this thing", blah, meh') |should| result_in(
            'def test_should_do_this_thing (blah ,meh ):raise nose.SkipTest '
            )

        # Same tests, but with newlines in front
        (self.toka, '\nit "should do this thing", blah, meh') |should| result_in(
            '\ndef test_should_do_this_thing (blah ,meh ):raise nose.SkipTest '
            )
        (self.tokb, '\nit "should do this thing", blah, meh') |should| result_in(
            '\ndef test_should_do_this_thing (blah ,meh ):raise nose.SkipTest '
            )

it has the ability to wrap setup and tearDown instead of inserting sup call

    def test_it_has_the_ability_to_wrap_setup_and_tearDown_instead_of_inserting_sup_call(self):
        self.toka.wrapped_setup = True
        self.tokb.wrapped_setup = True

        test = '''
        describe "Thing":
            after_each:
                self.x = 5

            describe "Other":
                before_each:
                    self.y = 8
        '''

        desired = '''
        class TestThing (object ):
            def tearDown (self ):
                self .x =5

        class TestThing_Other (TestThing ):
            def setUp (self ):
                self .y =8

        TestThing .tearDown =noy_wrap_tearDown (TestThing ,TestThing .tearDown )
        TestThing_Other .setUp =noy_wrap_setUp (TestThing_Other ,TestThing_Other .setUp )
        '''

        (self.toka, test) |should| result_in(desired)
        (self.toka, test.replace('    ', '\t')) |should| result_in(desired.replace('    ', '\t'))

it is possible to have indented block after setup with wrapped setup option

    def test_it_is_possible_to_have_indented_block_after_setup_with_wrapped_setup_option(self):
        self.toka.wrapped_setup = True
        self.tokb.wrapped_setup = True

        test = '''
        describe "Thing":
            after_each:
                def blah(self):
                    pass

            describe "Other":
                before_each:
                    class Stuff(object):
                        pass
        '''

        desired = '''
        class TestThing (object ):
            def tearDown (self ):
                def blah (self ):
                    pass

        class TestThing_Other (TestThing ):
            def setUp (self ):
                class Stuff (object ):
                    pass

        TestThing .tearDown =noy_wrap_tearDown (TestThing ,TestThing .tearDown )
        TestThing_Other .setUp =noy_wrap_setUp (TestThing_Other ,TestThing_Other .setUp )
        '''

        (self.toka, test) |should| result_in(desired)
        (self.toka, test.replace('    ', '\t')) |should| result_in(desired.replace('    ', '\t'))
Fork me on GitHub