def get_unisolated_imported_module(self): ''' get_module() should return a requested module, which should remain in sys.modules as it has been loaded unisolated. The same module should be returned on successive invocations. The same module should be returned from different contexts ''' same_context = ExecutionContext() same_context.add_import_path(self._nonsrc_path(same_context)) different_context = ExecutionContext(isolate_imports=False) different_context.add_import_path(self._nonsrc_path(different_context)) for mod in ('import_me', 'module_with_state'): spec = lancelot.Spec(same_context) spec.get_module(mod).should_be(Type(types.ModuleType)) same_module = same_context.get_module(mod) spec.get_module(mod).should_be(SameAs(same_module)) sys_spec = lancelot.Spec(sys.modules) sys_spec.it().should_contain(mod) different_module = different_context.get_module(mod) spec.get_module(mod).should_be(SameAs(different_module)) same_context.cleanup_imports() different_context.cleanup_imports()
def get_unisolated_module_type(self): ''' get_type() should return a requested module type, given the fully qualified name including module. The same type should be returned on successive invocations. The same type should also be returned from different contexts. State in a module should not be isolated within each context''' _types = {'StateAlteringClass': 'module_with_state'} execution_context = ExecutionContext(isolate_imports=False) execution_context.add_import_path(self._nonsrc_path(execution_context)) for _type, mod in _types.items(): fully_qualified_name = '%s.%s' % (mod, _type) same_type = execution_context.get_type(fully_qualified_name) lancelot.Spec(same_type.__name__).it().should_be(_type) spec = lancelot.Spec(execution_context) spec.get_type(fully_qualified_name).should_be(SameAs(same_type)) other_context = ExecutionContext(isolate_imports=False) other_context.add_import_path(self._nonsrc_path(other_context)) different_type = other_context.get_type(fully_qualified_name) spec.get_type(fully_qualified_name).should_be( SameAs(different_type)) instances = same_type(), different_type() spec = lancelot.Spec(instances[0]) spec.when(spec.alter_state()) spec.then(spec.get_state()).should_contain(1) spec.then(instances[1].get_state).should_contain(1) execution_context.cleanup_imports() other_context.cleanup_imports()
def params_must_be_list(self): ''' The params constructor arg must be a list ''' new_instance_with_bad_parms = lambda: Instruction('id', 'params') spec = lancelot.Spec(new_instance_with_bad_parms) spec.__call__().should_raise(TypeError) new_instance_with_list_parms = lambda: Instruction('id', ['params']) spec = lancelot.Spec(new_instance_with_list_parms) spec.__call__().should_not_raise(Exception)
def content_based_comparators(): ''' Illustrate use of should_contain() and should_not_contain() with first a str, then a dict''' camelot = 'a silly place' spec = lancelot.Spec(camelot) spec.split().should_contain('place') spec.split().should_not_contain('sequins') one_day_lad = {'all this': 'will be yours'} spec = lancelot.Spec(one_day_lad) spec.it().should_contain('all this') spec.it().should_not_contain('swamp')
def from_constructor_conversion(): ''' FromConstructorConverter converts using a type constructor, which is handy for int and float conversion ''' spec = lancelot.Spec(FromConstructorConverter(int)) spec.to_string(1).should_be('1') spec.to_string(2).should_be('2') spec.from_string('2').should_be(2) spec.from_string('1').should_be(1) spec = lancelot.Spec(FromConstructorConverter(float)) spec.to_string(3.141).should_be('3.141') spec.from_string('3.141').should_be(3.141)
def from_string_should_convert_items(self): ''' from_string() should convert types when required ''' spec = lancelot.Spec(IterableConverter(to_type=int)) spec.from_string('1,2, 3').should_be((1, 2, 3)) spec = lancelot.Spec(IterableConverter(using=YesNoConverter())) spec.from_string('yes, no,yes').should_be((True, False, True)) spec.from_string('no,yes, yes').should_be((False, True, True)) spec = lancelot.Spec(IterableConverter(to_type=(int, bool))) spec.from_string('1,yes').should_be((1, False)) spec = lancelot.Spec( IterableConverter(using=(YesNoConverter(), YesNoConverter()))) spec.from_string('1,No').should_be((False, False))
def convert_result_behaviour(): ''' convert_result() should decorate a method so that its return value is converted to a string using the supplied converter. If no converter is specified then a TypeError should be raised''' converter = FromConstructorConverter(int) decorated_fn = convert_result(using=converter)(ASystemUnderTest.add) sut = ASystemUnderTest() spec = lancelot.Spec(decorated_fn) spec.__call__(sut, 1, 3).should_be('4') spec.__call__(sut, 21, 1).should_be('22') spec = lancelot.Spec(convert_result) spec.convert_result(using=None).should_raise(TypeError)
def params_converter_behaviour(): ''' ParamsConverter should create (possibly nested) tuple of string args from a (possibly nested) list of strings (possibly symbols) ''' execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args([], 0).should_be(()) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['mint'], 0).should_be(('mint', )) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['wafer', 'thin'], 0).should_be(('wafer', 'thin')) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['wafer', 'thin'], 1).should_be(('thin', )) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['$A', '$b_', 'C$'], 0).should_collaborate_with( execution_context.get_symbol('A').will_return('X'), execution_context.get_symbol('b_').will_return('Y'), and_result=(('X', 'Y', 'C$'))) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['$id'], 0).should_collaborate_with( execution_context.get_symbol('id').will_return('20'), and_result=(('20', ))) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args([['bring', 'me'], ['another', 'bucket']], 0).should_be( (('bring', 'me'), ('another', 'bucket'))) execution_context = lancelot.MockSpec('execution_context') spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args(['$s11$s$s1$s11'], 0).should_collaborate_with( execution_context.get_symbol('s11').will_return('I'), execution_context.get_symbol('s').will_return('R'), execution_context.get_symbol('s1').will_return('O'), execution_context.get_symbol('s11').will_return('N'), and_result=(('IRON', ))) execution_context = lancelot.MockSpec('execution_context') long_multiline_expression = '''$a(long{ \ multi-line expression}) $and(another{ \ multi-line expression})''' spec = lancelot.Spec(ParamsConverter(execution_context)) spec.to_args([long_multiline_expression], 0).should_collaborate_with( execution_context.get_symbol('a').will_return('$a'), execution_context.get_symbol('and').will_return('$and'), and_result=(('%s' % long_multiline_expression, )))
def uses_added_import_paths(self): ''' add_import_path() should allow packages or modules to be found without altering sys.path''' execution_context = ExecutionContext() spec = lancelot.Spec(execution_context) spec.get_module('import_me').should_raise(ImportError) path = self._nonsrc_path(execution_context) spec.when(spec.add_import_path(path)) spec.then(spec.get_module('import_me')).should_not_raise(ImportError) lancelot.Spec(sys.path).it().should_not_contain(path) execution_context.cleanup_imports()
def handles_multiple_arg_types(self): ''' The decorator should handle conversion of multiple args of multiple types ''' multiply = ASystemUnderTest.multiply decorated_fn = convert_arg(to_type=(int, float))(multiply) sut = ASystemUnderTest() spec = lancelot.Spec(decorated_fn) spec.__call__(sut, '4', '1.2').should_be(FloatValue(4.8)) spec.__call__(sut, '3', '1.2').should_be(FloatValue(3.6)) converters = (FromConstructorConverter(int), FromConstructorConverter(float)) decorated_fn = convert_arg(using=converters)(multiply) spec = lancelot.Spec(decorated_fn) spec.__call__(sut, '3', '1.1').should_be(FloatValue(3.3))
def stores_instance(self): ''' store_instance(name, value) should put the name,value pair in the instances dict where it can be retrieved by get_instance(name). instances should be isolated across execution contexts''' context = ExecutionContext() spec = lancelot.Spec(context) spec.get_instance('wafer thin').should_be(None) spec.when(spec.store_instance('wafer thin', 'mint')) spec.then(spec.get_instance('wafer thin')).should_be('mint') spec = lancelot.Spec(ExecutionContext()) spec.get_instance('wafer thin').should_be(None) context.cleanup_imports()
def call_invokes_system_under_test(): ''' Will try to access sut when Call target has no such method''' params = ['instance', 'is_dead'] instances = [ ClassWithSystemUnderTestMethod(), ClassWithSystemUnderTestField() ] for instance in instances: execution_context = lancelot.MockSpec(name='execution_context') results = lancelot.MockSpec(name='results') call_instruction = Call('id_blah', params) spec = lancelot.Spec(call_instruction) spec.execute(execution_context, results).should_collaborate_with( execution_context.get_instance(params[0]).will_return(instance), execution_context.target_for(instance, params[1]).will_return(None), execution_context.target_for(instance, 'sut').will_return(instance.sut), execution_context.target_for(Anything(), params[1]).will_return(lambda: False), execution_context.to_args(params, 2).will_return(()), results.completed(call_instruction, False))
def import_adds_to_pythonpath(): ''' Import should add a path to the pythonpath when a path is supplied ''' execution_context = lancelot.MockSpec('execution_context') results = lancelot.MockSpec('results') import_instruction = Import('id', ['/some_path']) spec = lancelot.Spec(import_instruction) spec.execute(execution_context, results).should_collaborate_with( execution_context.add_import_path('/some_path'), results.completed(import_instruction)) execution_context = lancelot.MockSpec('execution_context') results = lancelot.MockSpec('results') import_instruction = Import('id', ['c:\some_path']) spec = lancelot.Spec(import_instruction) spec.execute(execution_context, results).should_collaborate_with( execution_context.add_import_path('c:\some_path'), results.completed(import_instruction))
def require_separators(self): ''' Unpacking a string without separators should raise an error ''' spec = lancelot.Spec(unpack) spec.unpack('[000000]').should_raise( UnpackingError("'[000000]' has no ':' separator at pos 7")) spec.unpack('[000001:000000]').should_raise( UnpackingError("'[000001:000000]' has no ':' separator at pos 14"))
def handles_incorrect_arg_lengths(self): ''' The decorator should handle incompatible arg lengths gracefully ''' divide_and_conquer = ASystemUnderTest.divide_and_conquer sut = ASystemUnderTest() decorated_fn = convert_arg(to_type=(int, float))(divide_and_conquer) spec = lancelot.Spec(decorated_fn) spec.__call__(sut, '9', '9.9', '9.99').should_raise(WaferSlimException)
def handles_false_return_values(self): ''' decorator should handle 'false' return values (0, [], ...) ''' add = ASystemUnderTest.add sut = ASystemUnderTest() decorated_fn = convert_arg(to_type=(int, int))(add) spec = lancelot.Spec(decorated_fn) spec.__call__(sut, '0', '0').should_be(0)
def handles_stoptest_exceptions(self): ''' if stop test exception is thrown stop executing instructions ''' mock_fn = lancelot.MockSpec(name='mock_fn') mock_call = lancelot.MockSpec(name='mock_call') results = lancelot.MockSpec(name='results') a_list = [['id_', 'call', 'instance', 'fn', 'arg'], ['id_', 'call', 'instance2', 'fn2', 'arg2']] instructions = Instructions(a_list, lambda item: mock_fn.instruction_for(item)) spec = lancelot.Spec(instructions) ctx = ExecutionContext() msg = "I couldn't eat another thing. I'm absolutely stuffed." # Suppress warning log message that we know will be generated logger = logging.getLogger('Instructions') log_level = logger.getEffectiveLevel() logger.setLevel(logging.ERROR) try: spec.execute(ctx, results).should_collaborate_with( mock_fn.instruction_for(a_list[0]).will_return(mock_call), mock_call.execute(ctx, results).will_raise(StopTestException(msg)), results.failed(mock_call, msg, True)) finally: # Put logger back to how it was logger.setLevel(log_level) ctx.cleanup_imports()
def default_converter_for(): ''' If no registered converter exists for the type of value being converted then the base Converter class should be used. ''' fake = Fake('And a double Jeroboam of champagne') spec = lancelot.Spec(converter_for) spec.converter_for(fake).should_be(Type(Converter)) spec.converter_for(Fake).should_be(Type(Converter))
def execute_fails(self): ''' The base class execute() method fails with INVALID_STATEMENT ''' instruction = Instruction('id', ['nonsense']) spec = lancelot.Spec(instruction) execution_context = lancelot.MockSpec('execution_context') results = lancelot.MockSpec('results') spec.execute(execution_context, results).should_collaborate_with( results.failed(instruction, 'INVALID_STATEMENT nonsense'))
def completed_ok(self): ''' completed() for Make should add ok to results list. Results list should be accessible through collection() ''' instruction = lancelot.MockSpec(name='instruction') spec = lancelot.Spec(Results()) spec.completed(instruction).should_collaborate_with( instruction.instruction_id().will_return('a')) spec.collection().should_be([['a', 'OK']])
def returns_callable_to_type(self): ''' decorator for 'to_type' should return a callable that takes 2 or more args. Invoking that callable should convert each arg.''' decorated_fn = convert_arg(to_type=float)(ASystemUnderTest.set_afloat) spec = lancelot.Spec(decorated_fn) spec.__call__('1.99').should_raise(TypeError) # only 1 arg sut = ASystemUnderTest() spec.__call__(sut, '1.99').should_not_raise(TypeError) spec.when(spec.__call__(sut, '2.718282')) spec.then(lambda: sut.float_value).should_be(2.718282) decorated_fn = convert_arg(to_type=int)(ASystemUnderTest.add) spec = lancelot.Spec(decorated_fn) spec.__call__(sut, '1', '2').should_be(3)
def length_based_comparators(): ''' Illustrate Empty and Length comparators with both str and list. Also illustrates use of spec.it() ''' spec = lancelot.Spec('huge...') spec.join([]).should_be(Empty()) spec.join(['tracts of land']).should_be(Length(14)) castles = [ 'sank into the swamp', 'sank into the swamp', 'burned down, fell over, then sank into the swamp', 'stayed up' ] spec = lancelot.Spec(castles) spec.when(spec.remove('sank into the swamp')) spec.then(spec.it()).should_be(Length(3)) spec.when(spec.pop(), spec.pop(), spec.pop()) spec.then(spec.it()).should_be(Empty())
def completed_without_return_value(self): ''' completed() should add to results list. Results list should be accessible through collection() ''' instruction = lancelot.MockSpec(name='instruction') spec = lancelot.Spec(Results()) spec.completed(instruction, result=None).should_collaborate_with( instruction.instruction_id().will_return('c')) spec.collection().should_be([['c', '/__VOID__/']])
def registered_converter_for_(): ''' converter_for() should supply the registered converter for the type of value being converted if it exists. ''' fake = Fake('I think I can only manage six crates today') register_converter(Fake, Converter()) spec = lancelot.Spec(converter_for) spec.converter_for(fake).should_be(Type(Converter)) spec.converter_for(Fake).should_be(Type(Converter))
def require_square_brackets(self): ''' Unpacking a string without a leading square bracket, or a string without an ending square bracket should raise an error ''' spec = lancelot.Spec(unpack) spec.unpack('').should_raise(UnpackingError("'' has no leading '['")) spec.unpack('[hello').should_raise( UnpackingError("'[hello' has no trailing ']'")) spec.unpack('hello]').should_raise( UnpackingError("'hello]' has no leading '['"))
def get_type_all_lowercaps(self): ''' get_type(namewithoutspaces) => get_type (Namewithoutspaces) ''' context = ExecutionContext() context.add_type_prefix('waferslim.examples.helper_fixtures') multiplication = context.get_type('Multiplication') spec = lancelot.Spec(context) spec.get_type('multiplication').should_be(multiplication) context.cleanup_imports()
def time_converter_behaviour(): ''' TimeConverter should convert to/from datetime.date type using iso-standard format (2digithour:2digitminute:2digitsecond - with or without an additional optional .6digitmillis)''' spec = lancelot.Spec(TimeConverter()) spec.to_string(datetime.time(1, 2, 3)).should_be('01:02:03') spec.to_string(datetime.time(1, 2, 3, 4)).should_be('01:02:03.000004') spec.from_string('01:02:03').should_be(datetime.time(1, 2, 3)) spec.from_string('01:02:03.000004').should_be(datetime.time(1, 2, 3, 4))
def imports_twisted(self): ''' Bug #497245: cannot import twisted ''' from os.path import join, exists for location in sys.path: pkg = join(location, join('twisted', '__init__.py')) if exists(pkg) \ or exists(pkg + 'c') \ or exists(pkg + 'o'): twisted_found = True break lancelot.Spec(twisted_found).it().should_be(True) context = ExecutionContext(isolate_imports=False) #TODO: isolated?! context.add_import_path(self._nonsrc_path(context)) spec = lancelot.Spec(context) spec.get_module('import_twisted').should_be(Type(types.ModuleType)) spec.get_module('twisted').should_be(Type(types.ModuleType)) context.cleanup_imports()
def failed(self): ''' failed() should add a translated error message to results list. Results list should be accessible through collection() ''' formatted_cause = '__EXCEPTION__: message:<<bucket>>' instruction = lancelot.MockSpec(name='instruction') spec = lancelot.Spec(Results()) spec.failed(instruction, 'bucket') spec.should_collaborate_with( instruction.instruction_id().will_return('b')) spec.collection().should_be([['b', formatted_cause]])
def failed_with_stoptest(self): ''' failed(stop_test=True) should change formatted cause to include the stop message ''' formatted_cause = '__EXCEPTION__:ABORT_SLIM_TEST: message:<<bucket>>' instruction = lancelot.MockSpec(name='instruction') spec = lancelot.Spec(Results()) spec.failed(instruction, 'bucket', True) spec.should_collaborate_with( instruction.instruction_id().will_return('b')) spec.collection().should_be([['b', formatted_cause]])