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.
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'})
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 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({})
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 ')
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, '*')
])
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)
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)
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 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)
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'))