def run(self): """Runs the current test case. Steps taken to run test are 1. Create empty step context 2. Load first step 3. Update context for current step 4. Run middlewares for 'BEFORE_STEP' 5. Dispatch step to middlewares 6. Run middlewares for 'AFTER_STEP' 7. Update current Step 8. Go to step 3. Unless last step Raises: ImproperlyConfigured -- If the test is not ready, this is raised """ if not self.ready(): raise ImproperlyConfigured( "Cannot start testcase because the test is not ready") step_context = StepContext(self) while self.current_step is not None: # End condition when no more steps # Step 1. Update context step_context.update_context(self.current_step, step_context) # Step 2. Run middlewares for `before_step` self.run_middlewares(step_context, MIDDLEWARE_MODE_BEFORE_STEP) # Step 3. Run Step try: self.execute_step(step_context) except Exception as e: step_context.step_settings["Exception"] = e self.run_middlewares(step_context, MIDDLEWARE_MODE_STEP_FAILURE) break # Step 4. Run middleswares for `after_step` self.run_middlewares(step_context, MIDDLEWARE_MODE_AFTER_STEP) # Step 5. Update step self.current_step = step_context.next_step # Prepare TearDown Steps self.current_step = self.tear_down_first_step # Step 6. Run TearDown steps while self.current_step is not None: step_context.update_context(self.current_step, step_context) self.run_middlewares(step_context, MIDDLEWARE_MODE_TEARDOWN_BEFORE_STEP) self.execute_step(step_context) self.run_middlewares(step_context, MIDDLEWARE_MODE_TEARDOWN_AFTER_STEP) self.current_step = step_context.next_step
def _get_step_result_by_tag(self, first_step, tag): current_step = first_step while True: tag_value = getattr(current_step, "tag", None) if tag_value == tag: return lambda: current_step.get_result() if current_step.is_last_step(): break current_step = current_step.next_step raise ImproperlyConfigured(f"No step found with tag {tag}")
def execute_step(self, step_context): action_interface = step_context.action_interface # If a step has a defined interface or not if action_interface is None: for interface in self.interfaces: # Go through each interface try: self.interfaces[interface].dispatch(step_context) break except ImproperlyConfigured: # Interface does not define action continue else: raise ImproperlyConfigured( f"No interface found that defines {step_context.step_action}" ) else: if action_interface not in self.interfaces: raise ImproperlyConfigured( f"Interface {action_interface} is not installed") self.interfaces[action_interface].dispatch(step_context)
def load_testloader(self, loader_name, loader_module) -> None: loader_path = ".".join([loader_module, "loader_entry"]) loader_entry = load_module(loader_path) loader = load_module(loader_entry)() if not isinstance(loader, TBBaseTestLoader): raise ImproperlyConfigured( "TestLoader is not of instance TBBaseTestLoader") self.testloaders[loader_name] = loader
def load_objectmap_parser(self, parser_name, parser_module) -> None: parser_path = ".".join([parser_module, "objectmap_parser_entry"]) parser_entry = load_module(parser_path) parser = load_module(parser_entry)() if not isinstance(parser, TBBaseObjectMapParser): raise ImproperlyConfigured( "Objectmap parser is not of instance TBBaseObjectMapParser") self.objectmap_parsers[parser_name] = parser
def load_middleware(self, middleware_name, middleware_module) -> None: middleware_path = ".".join([middleware_module, "middleware_entry"]) middleware_entry = load_module(middleware_path) middleware = load_module(middleware_entry) if not issubclass(middleware, TBBaseMiddleware): raise ImproperlyConfigured( "Middleware does not derive from TBBaseMiddleware") self.middlewares[middleware_name] = middleware
def load_interface(self, interface_name, interface_module) -> None: interface_path = ".".join([interface_module, "interface_entry"]) interface_entry = load_module(interface_path) interface = load_module(interface_entry) if not issubclass(interface, TBBaseInterface): raise ImproperlyConfigured( "Interface does not derive from TBBaseInterface") self.interfaces[interface_name] = interface
def load_object_map(self, object_map, object_map_name) -> None: """Registers an objectmap for the test Arguments: object_map {TBBaseObjectMap} -- The objectmap instance object_map_name {str} -- Name of the objectmap """ if not isinstance(object_map, TBBaseObjectMap): raise ImproperlyConfigured( "Objectmap is not of instance TBBaseObjectMap") self.object_maps[object_map_name] = object_map
def load_middleware(self, middleware) -> None: """Creates and appends middleware to list of middleswares. Note that the order in which this function is called has a role in how the script may run. Arguments: middleware {TBBaseMiddleware} -- Class of TBMiddlware, **not instance of** """ if not issubclass(middleware, TBBaseMiddleware): raise ImproperlyConfigured( "Middleware is not of subclass TBBaseMiddleware") self.middlewares.append(middleware()) # Create and append middleware
def create_tests(self, test_location, loader_name, profile_name): if loader_name not in self.testloaders: raise ImproperlyConfigured( f"No testloader installed named {loader_name}") if profile_name not in self.profiles: raise ImproperlyConfigured( f"No profile installed named {profile_name}") # Returns a list of tests from file tests = self.testloaders[loader_name].load_tests(test_location) for test in tests: profile = self.profiles[profile_name] ## Step 1. Load middlewares if "middlewares" in profile: for middleware_name in profile["middlewares"]: middleware = self.middlewares[middleware_name] test.load_middleware(middleware) ## Step 2. Load interfaces for interface_name, interface in self.interfaces.items(): test.load_interface(interface, interface_name) ## Step 3. Load objectmap for objectmap_name in settings["OBJECT_MAPS"]: obj_parser_name, obj_location = settings["OBJECT_MAPS"][ objectmap_name] if obj_parser_name not in self.objectmap_parsers: raise ImproperlyConfigured( f"ObjectMap Parser {obj_parser_name} is not installed") obj_parser = self.objectmap_parsers[obj_parser_name] object_map = obj_parser.parse(objectmap_name, obj_location) test.load_object_map(object_map, objectmap_name) return tests
def load_interface(self, interface, interface_name) -> None: """Creates and registers a interface for the test Arguments: interface {TBBaseInterface} -- Class of TBBaseInterface, **not instance of** interface_name {String} -- Name of the interface Raises: ImproperlyConfigured -- Raised if interface not of subclass TBBaseInterface """ if not issubclass(interface, TBBaseInterface): raise ImproperlyConfigured( "Interface is not of subclass TBBaseInterface") self.interfaces[interface_name] = interface( ) # Create and register interface
def _step_field_from_fixture(self, field, test): _, _, fixture_info = field.partition(".") f0 = fixture_info.find('[') fixture_name = fixture_info[0:f0] fixture_column = fixture_info[f0 + 2:-2] fixture = test.fixtures.get(fixture_name) if fixture is None: raise ImproperlyConfigured( f"Test does not contain fixture{fixture_name}") return lambda: fixture.get_value(test.get_current_iteration(), fixture_column)
def load_fixtures(self, fixture, fixture_name=None): """Load fixture to testcase Arguments: fixture {TBBaseFixture} -- Fixture instance Keyword Arguments: fixture_name {str} -- Optional name of fixture (default: {None}) Raises: ImproperlyConfigured -- Raised if fixture is not of subclass TBBaseFixture """ if not isinstance(fixture, TBBaseFixture): raise ImproperlyConfigured( "Expected 'fixture' to be of class TBBaseFixture") if fixture_name is None: self.fixtures[fixture.fixture_name] = fixture else: self.fixtures[fixture_name] = fixture
def dispatch(self, step_context): """Attempts to dispatch a action to the interface Arguments: step_context {StepContext} -- The current step context Raises: ImproperlyConfigured -- Raised if interface does not define the action specified by `step_context.step_action` """ action = step_context.step_action defined_action_words = self.get_list_of_action_words() if action in defined_action_words: try: getattr(self, action)(step_context) # Run step self._pass_step(step_context) # Mark step as passed except Exception as e: self._fail_step(step_context, e) # Mark step as failed raise else: raise ImproperlyConfigured(f"Interface does not have a defined action {action}")
def load_tests(self, test_location) -> List[TBBaseTest]: """Parse and add a test to tests Arguments: path {str} -- The path to the yaml test case """ if not os.path.exists(test_location): raise ImproperlyConfigured(f"Path does not exist: {test_location}") yaml_test_data = None with open(test_location) as f: yaml_test_data = yaml.load(f) # Check that test steps exists test_steps = yaml_test_data.get("steps") if not test_steps: raise ImproperlyConfigured("Testcase has no steps, stopping") # Create base test with properties test_properties = yaml_test_data.get( "properties", {}) # Load property mapping, or empty dict type test = TBBaseTest(**test_properties) # load all properties to test # Load all additional properties to testcase test_additional_properties = yaml_test_data.get( "additional_properties") if test_additional_properties: for setting in test_additional_properties: # Add all settings to testcase value = test_additional_properties[setting] test.load_additional_property(setting, value) # Load test fixtures test_fixtures = yaml_test_data.get("fixtures") if test_fixtures: for fixture_name in test_fixtures: # Iterate over each fixture in testcase fixture_path = test_fixtures[fixture_name] if not os.path.exists( fixture_path): #If fixture is relative path fixture_path = os.path.join( os.path.dirname(test_location), # Testcase path fixture_path # Fixture path relative to testcase ) fixture = CSVFixture(fixture_name, fixture_path) # Create fixutre test.load_fixtures(fixture) # Load fixture in test first_step = None _cur_step = None # Load test steps for i, step in enumerate(test_steps): if i == 0: # First step first_step = self.load_test_step(step, test) _cur_step = first_step continue next_step = self.load_test_step(step, test, first_step) # Load the next step _cur_step.add_next_step(next_step) # Register the next step _cur_step = next_step # Replace current step with next step test.load_steps(first_step) # Load teardown steps tear_down_steps = yaml_test_data.get("tear_down") first_step = None _cur_step = None # Load test steps if tear_down_steps is not None: for i, step in enumerate(tear_down_steps): if i == 0: # First step first_step = self.load_test_step(step, test) _cur_step = first_step continue next_step = self.load_test_step( step, test, first_step) # Load the next step _cur_step.add_next_step(next_step) # Register the next step _cur_step = next_step # Replace current step with next step test.load_tear_down_steps(first_step) return [test]