def __init__(self, host, user, password, file_path, section_id, file_format='robot'): """ :param host: test rail host :param user: user name :param password: password :param file_path: path of test case files or directory :param section_id: section to store auto test cases :param file_format: default to be .robot """ testrail_url = 'http://' + host + '/testrail/' self.client = APIClient(testrail_url, user, password) self.file = list() # to loop through the test suites try: if os.path.isdir(file_path): for files in os.walk(file_path): for robot_file in filter(lambda x: x.endswith('.robot'), files[2]): self.file.append( TestData(source=os.path.abspath( os.path.join(files[0], robot_file)))) else: self.file.append(TestData(source=file_path)) except DataError as e: # .robot file may have no test cases in it logging.warn('[TestRailTagger]' + e.message) self.section = section_id self.writer = DataFileWriter(format=file_format)
def main(): # Declare TestRail API testrail_api = APIClient(TESTRAIL_URL) testrail_api.user = TESTRAIL_EMAIL_ADDRESS testrail_api.password = TESTRAIL_API_TOKEN # Retrieve all sections for the project all_sections = testrail_api.send_get('get_sections/' + str(PROJECT_ID)) # all_results will hold all test results and will be the body of the result submission to TestRail all_results = dict() all_results['results'] = list() all_results['results'] += run_tests(all_sections, testrail_api) # Create test run new_run = testrail_api.send_post('add_run/' + str(PROJECT_ID), { "name": "Webinar Math Tests", "include_all": True }) # add results run_results = testrail_api.send_post( 'add_results_for_cases/' + str(new_run['id']), all_results) # Close test run testrail_api.send_post('close_run/' + str(new_run['id']), {}) # Display some stuff # print(all_results) print('Testing Complete!\nResults available here: ' + TESTRAIL_URL + 'index.php?/runs/view/' + str(new_run['id'])) pass
def __init__(self, run_id, update = None): """ *Args:*\n _server_ - the name of TestRail server; _user_ - the name of TestRail user; _password_ - the password of TestRail user; _run_id - the ID of the test run; _update_ - indicator to update test case in TestRail; if exist, then test will be updated. """ user = '******' password = '******' url = 'https://csod.testrail.net' testrail_url = url self.client = APIClient(testrail_url) self.client.user = user self.client.password = password self.run_id = run_id self.update = update self.results = list() logger.info('[TestRailListener] url: ' + testrail_url) logger.info('[TestRailListener] user: '******'[TestRailListener] password: '******'[TestRailListener] the ID of the test run: ' + run_id)
def __init__(self, host, user, password, file_path, section_id, file_format='robot'): """ :param host: test rail host :param user: user name :param password: password :param file_path: path of test case files or directory :param section_id: section to store auto test cases :param file_format: default to be .robot """ testrail_url = 'http://' + host + '/testrail/' self.client = APIClient(testrail_url, user, password) self.file = list() # to loop through the test suites try: if os.path.isdir(file_path): for files in os.walk(file_path): for robot_file in filter(lambda x: x.endswith('.robot'), files[2]): self.file.append( TestData( source=os.path.abspath(os.path.join(files[0], robot_file)) ) ) else: self.file.append(TestData(source=file_path)) except DataError as e: # .robot file may have no test cases in it logging.warn('[TestRailTagger]' + e.message) self.section = section_id self.writer = DataFileWriter(format=file_format)
def __init__(self, username='', password='', url=''): self.username = username self.password = password self.url = url if not username: self.username = settings.USR if not password: self.password = settings.PWD if not url: self.url = settings.URL client = APIClient(self.url) client.user = self.username client.password = self.password self.client = client self.parser = None
def __init__(self, server, user, password, run_id, update = None): """ *Args:*\n _server_ - the name of TestRail server; _user_ - the name of TestRail user; _password_ - the password of TestRail user; _run_id - the ID of the test run; _update_ - indicator to update test case in TestRail; if exist, then test will be updated. """ testrail_url = 'http://' + server + '/testrail/' self.client = APIClient(testrail_url) self.client.user = user self.client.password = password self.run_id = run_id self.update = update self.results = list() logger.info('[TestRailListener] url: ' + testrail_url) logger.info('[TestRailListener] user: '******'[TestRailListener] password: '******'[TestRailListener] the ID of the test run: ' + run_id)
def __init__(self, tr_server, user_name, password): """ This method will initialize the TestRail server using the user name and password provided :param tr_server: Name of the Test Rail server :param user_name: TestRail user id :param password: TestRail password """ file_dir = os.path.split(os.path.realpath(__file__))[0] logging.config.fileConfig(os.path.join(file_dir, "trlogger.ini")) # Configure the logger self._log = logging.getLogger('testrail') self._log.info("Starting TestRail application ") # TestRail Connection status # Note: This variable is used to ensure we have a valid TestRail Instance self._connection_status = False try: # Check if the URL is valid self._client = APIClient(tr_server) self._client.password = password self._client.user = user_name self._connection_status = True # Check if the url, user name and password is set correctly by accessing an API self._client.send_get(Defines.TR_API_GET_PROJ + "/" + str(Defines.TR_PROJ_ID_OTG)) self._log.info( "Connected to TestRail server {} with user-id {}".format( tr_server, user_name)) except err.URLError: self._log.exception("Url: {} is not valid".format(tr_server)) raise err.URLError("Error: Please check the URL") except APIError: self._log.critical( "User-id or Password is not correct. Failed to connect with TestRail url: {}" .format(tr_server)) raise APIError("Error: Please check user id and password")
from testrail import APIClient import sys #connection information client = APIClient('https://testplant.testrail.net/') client.user = '******' client.password = '******' #open the RunHistory.csv file, then check if the last line includes the word "Success" #(The latest result is in the last line) #f = open('C:/Workspaces/ePF/TestRailSample/TestRail_sample.suite/Results/main/RunHistory.csv') resultFolder = sys.argv[1] print resultFolder sys.exit() lastLine = f.readlines()[-1] # Read the last line of the results file f.close() fields = lastLine.split(',') # Split the line into comma separated fields if len(fields) != 10: print("Error: Last line of RunHistory has fewer than 10 fields") sys.exit() if fields[1].find('Success') >= 0: print ('Last Run: Success') result = client.send_post( 'add_result_for_case/1/1', {'status_id': 1, 'comment': fields[8]} ) elif fields[1].find ('Failure') > 0:
class TestRailTagger(object): """ class to register robot's test cases in test rail and then add corresponding tags to robot's test cases """ def __init__(self, host, user, password, file_path, section_id, file_format='robot'): """ :param host: test rail host :param user: user name :param password: password :param file_path: path of test case files or directory :param section_id: section to store auto test cases :param file_format: default to be .robot """ testrail_url = 'http://' + host + '/testrail/' self.client = APIClient(testrail_url, user, password) self.file = list() # to loop through the test suites try: if os.path.isdir(file_path): for files in os.walk(file_path): for robot_file in filter(lambda x: x.endswith('.robot'), files[2]): self.file.append( TestData( source=os.path.abspath(os.path.join(files[0], robot_file)) ) ) else: self.file.append(TestData(source=file_path)) except DataError as e: # .robot file may have no test cases in it logging.warn('[TestRailTagger]' + e.message) self.section = section_id self.writer = DataFileWriter(format=file_format) def add_tag(self): """ add specific tags to test cases """ for suite in self.file: # to handle force tags, delete force tags in setting table # and then add the tag value to each test case in a suite force_tags = suite.setting_table.force_tags.value suite.setting_table.force_tags.value = None for test in suite.testcase_table.tests: getattr(self, TAG_TYPE['testRail'])(test, suite, force_tags) def add_test_rail_id(self, test, test_case_file, other_tags): """ register test case and add the test case id to .robot file :param test: TestData class object, one of test cases read from goven .robot file :param test_case_file: the .robot file to write (add tags) :param other_tags: param for the force tags(also for other tags) """ if test.tags.value is None: test.tags.value = list() tags_value = getTagsValue(test.tags.value) case_id = tags_value.get('testRailId') if case_id is None: res = self.client.send_post( 'add_case/{section_id}'.format(section_id=self.section), { 'title': 'Auto-' + test.name, 'type_id': 1, 'priority_id': 3, 'estimate': '1h', 'refs': '', 'custom_steps': [ { 'content': getattr(step, 'name', ''), 'expected': 'auto-results' } for step in test.steps if step ] } ) case_id = res.get('id') logging.info('[TestRailTagger] register test {case_id} to TestRail' .format(case_id=case_id)) test.tags.value.append(TEST_RAIL_ID_TEMPLATE.format(case_id=case_id)) test.tags.value += other_tags self.writer.write(test_case_file)
class TestRailLib: """ This class contains methods for accessing TestRail API's. For more information : https://www.gurock.com/testrail """ def __init__(self, tr_server, user_name, password): """ This method will initialize the TestRail server using the user name and password provided :param tr_server: Name of the Test Rail server :param user_name: TestRail user id :param password: TestRail password """ file_dir = os.path.split(os.path.realpath(__file__))[0] logging.config.fileConfig(os.path.join(file_dir, "trlogger.ini")) # Configure the logger self._log = logging.getLogger('testrail') self._log.info("Starting TestRail application ") # TestRail Connection status # Note: This variable is used to ensure we have a valid TestRail Instance self._connection_status = False try: # Check if the URL is valid self._client = APIClient(tr_server) self._client.password = password self._client.user = user_name self._connection_status = True # Check if the url, user name and password is set correctly by accessing an API self._client.send_get(Defines.TR_API_GET_PROJ + "/" + str(Defines.TR_PROJ_ID_OTG)) self._log.info( "Connected to TestRail server {} with user-id {}".format( tr_server, user_name)) except err.URLError: self._log.exception("Url: {} is not valid".format(tr_server)) raise err.URLError("Error: Please check the URL") except APIError: self._log.critical( "User-id or Password is not correct. Failed to connect with TestRail url: {}" .format(tr_server)) raise APIError("Error: Please check user id and password") def __extract_dictionary(self, src_dict, extract_list, dict_type=Defines.DICT_SUB, ret_dict_key=Defines.TR_TP_ID): """ This method will extract and create a new dictionary based on the attributes passed. The extraction can be done on a single plain dictionary or dictionary within a dictionary. Note: The ret_dict_key must be an unique identifier as its the ret_dict key. :param src_dict: Source dictionary :param extract_list: List of keys to be extracted :param dict_type: This parameter determines if we need to extract values from simple dictionary or sub dictionaries :param ret_dict_key: Key that must be used for return dictionary :return: List of dictionary with ret_dict_key as key. Example output ret_dict = { 1:{'description':'Example 1'} , 2: {'description': 'Example 2} } """ if extract_list: # Extracting list containing dictionary of dictionary ( sub dictionary) # Example [ 1:{'description':'Example 1'} , 2: {'description': 'Example 2} ] if dict_type in Defines.DICT_SUB: ret_dict = [] # This code applies on sub dictionary for src_key in src_dict: tmp_list = {} for extract_key in extract_list: # Check if the key is present in source dictionary ( dictionary of dictionary) if extract_key in src_key: tmp_list[extract_key] = src_key[extract_key] else: self._log.info( "{} is invalid key or not present in the source dictionary" .format(extract_key)) # update the return dictionary with key as the plan id if dict_type in Defines.DICT_SUB: ret_dict.append({src_key[ret_dict_key]: tmp_list}) else: ret_dict.append(tmp_list) else: # Extracting items from Plain dictionary # In simple scenario it will return a dictionary not a list ret_dict = {} for extract_key in extract_list: if extract_key in src_dict: ret_dict[extract_key] = src_dict[extract_key] else: self._log.debug("Nothing to extract. All items will be returned") ret_dict = src_dict return ret_dict def count_values_in_sub_dict(self, dict_list, grp_item_key): """ This method will group and count the keys of a sub dictionary See the example in the doc string for output :param dict_list: Input dictionary :param grp_item_key: key which needs to be grouped :return: count of the values in the sub dictionary Example : [ A:{color:'a'}, B:{color:'b'}, C:{color:'a'}, D:{color:'a'}] output: {a:3, b1} """ ret_dict = {} cnt = 1 try: for list_items in dict_list: for key, value in list_items.items(): if value[grp_item_key] in ret_dict: ret_dict[value[grp_item_key]] = ret_dict[ value[grp_item_key]] + 1 else: ret_dict[value[grp_item_key]] = cnt except KeyError: self._log.exception("KeyError Exception for key ") return ret_dict def tr_api_post(self, api, params): """ This method is a wrapper method to call the POST methods of TestRail :param api: Test Rail API :param params: Post parameters for the Test Rail API. :return: True, message or False, Message True with and the return message will vary depending on the api requested False with and the return message will be the exception caught """ status = False try: return_msg = self._client.send_post(api, params) status = True self._log.info( "Successfully sent data using POST method. API = {} Data = {}". format(api, params)) except APIError as e: self._log.exception( "API Exception for api POST {} - Exception: {}".format(api, e)) return_msg = e return status, return_msg def tr_api_get(self, api, params): """ This method is a wrapper method to call the GET methods of TestRail apends the TestRail API and the parameters. :param api: TestRail api to be called ( Example: get_case) :param params: The GET parameters for API Methods :return: True, message or False, Message True with and the return message will vary depending on the api requested False with and the return message will be the exception caught """ status = False try: return_msg = self._client.send_get(api + "/" + str(params)) status = True self._log.info( "Successfully sent data using GET method. API = {} Data = {}". format(api, params)) except APIError as e: self._log.exception( "API Exception for api GET {} - Exception: {}".format(api, e)) return_msg = e return status, return_msg # Methods for TestRail Test Plans def tr_get_all_plans(self, project_id, project_fields): """ This method will extract all the plans for a project. We can specify what attribute of TestRail project We want to extract using project_fields. :param project_id: Project id ( Example 1) :param project_fields: This is a list of fields needed for project Example: project passed count, failed count etc :return: status( Boolean) and ret_list : list of Plans with attributes from project_fields """ ret_dict = None status, ret_list = self.tr_api_get(Defines.TR_API_GET_PLANS, project_id) if status: ret_dict = self.__extract_dictionary(ret_list, project_fields) return status, ret_dict def tr_get_tc_suites_in_project(self, project_id=Defines.TR_CURRENT_PROJECT, extract_list=None): """ This method will get the suites available in the TestRail project :param project_id: Project id :param extract_list: The attribute required to be extracted from test suites :return: Dictionary of project test suites with the attributes mentioned in extract_list """ # If the extract list is none at least extract the description if extract_list is None: extract_list = [Defines.TR_TS_DESCRIPTION] ret_dict = {} ret_status, suite_list = self.tr_api_get(Defines.TR_API_GET_SUITES, project_id) if ret_status: ret_dict = self.__extract_dictionary(suite_list, extract_list, ret_dict_key=Defines.TR_TS_ID) else: self._log.error("Error in getting test suites") return ret_status, ret_dict def tr_get_tescase_in_run(self, run_id, extract_list=None): """ This method will extract all the test for a run. You can pass what you want to extract using extract_list. :param run_id: Test run id :param extract_list: attributes that needs to be extracted :return: list of dictionaries containing the test case information """ ret_list = [] # if the extract list is None. Extract the Description and automated fields if extract_list is None: extract_list = [ Defines.TR_TC_CUSTOM_TEST_DESCRIPTION, Defines.TR_TC_CUSTOM_AUTOMATED ] status, tc_list = self.tr_api_get(Defines.TR_TESTS_GET_TESTS, run_id) if status: # For each test extract the required elements for tst in tc_list: ret_list.append( self.__extract_dictionary(tst, extract_list, dict_type=Defines.DICT_SIMPLE)) self._log.info( "Extracted test cases successfully from run {}".format(run_id)) else: self._log.error( "Error in extracting the test cases for run {}".format(run_id)) return status, ret_list def tr_get_test_cases(self, suite_id, extract_list=None): """ This method will get all the test cases of a suite. A suite in TestRail contains collection of test cases :param suite_id: suite id from TestRail :param extract_list: attribuites needed to extract for each test case. Use the TR_TC_XXXX attributes :return: Status , dictionary of test cases available for the suite """ ret_dict = {} # if the extract list is none extract the description and created by fields if extract_list is None: extract_list = [ Defines.TR_TC_CUSTOM_TEST_DESCRIPTION, Defines.TR_TC_CREATED_BY ] qry_str = str(Defines.TR_CURRENT_PROJECT ) + "&" + Defines.TR_TS_SUITE_ID + "=" + str(suite_id) status, ret_list = self.tr_api_get(Defines.TR_API_GET_CASES, qry_str) if status: ret_dict = self.__extract_dictionary(ret_list, extract_list, ret_dict_key=Defines.TR_TC_ID) else: self._log.error("Error in getting the test case") return status, ret_dict def tr_get_test_case_info(self, tc_id, extract_list=None): ret_dict_list = None # Extract title as default value if extract_list is None: extract_list = [Defines.TR_TC_TITLE] status, ret_list = self.tr_api_get(Defines.TR_API_GET_CASE, tc_id) if status: ret_dict_list = self.__extract_dictionary( ret_list, extract_list, dict_type=Defines.DICT_SIMPLE, ret_dict_key=Defines.TR_TC_ID) else: self._log.error("Error in executing API {}".format( Defines.TR_API_GET_CASE)) return status, ret_dict_list def tr_get_test_plan( self, plan_id, ): """ This method will get the plan in a test run. A run id start with RXXXX format in TestRail :param plan_id: :return: """ status, ret_msg = self.tr_api_get(Defines.TR_API_GET_PLAN, plan_id) if not status: self._log.error( "Error while getting the plan details for plan {}".format( plan_id)) return status, ret_msg def tr_get_testsuite_info_in_test_plan(self, plan_id): """ This method will get the list of test suites in a test plan. The return dictionary will also contain the config information of a test suite. :param plan_id: :return: """ # Testplan - > Test runs -> config ret_dict_list = [] # First get the test plan details. Test plan will contain test suites. # Test suites can have multiple Configurations status, ret_msg = self.tr_get_test_plan(plan_id) if status: # Get the runs of the test plan. This can be extracted using the entries field in the test plan tp_entries = ret_msg[Defines.TR_TP_ENTRIES] if tp_entries: # For each entries ( Test suites) get the suite information and the config information. # Ex: a test suite A can be tested on different configs like iOS or Android for entries_item in tp_entries: runs = entries_item[Defines.TR_TP_RUNS] for run in runs: tmp = { run[Defines.TR_TS_ID]: { Defines.TR_TP_SUITE_ID: run[Defines.TR_TP_SUITE_ID], Defines.TR_TP_TR_CONFIG: run[Defines.TR_TP_TR_CONFIG], Defines.TR_TP_TR_CONFIG_IDS: run[Defines.TR_TP_TR_CONFIG_IDS] } } ret_dict_list.append(tmp) else: # The plan has no suites. Make an entry in the logger self._log.error( "Test plan has no test suites selected. Check and add test suites to the test plan {}" .format(plan_id)) else: self._log.error( "Error in calling tr_get_test_plan for the run_id-{}".format( plan_id)) return status, ret_dict_list def tr_get_testcase_in_test_plan(self, plan_id, extract_list=None): """ This method will collect the test in a test plan using the test plan id and segregates into automated and manual test cases. :param plan_id: Test plan id :param extract_list: Attribute which need to be extracted :return: status (Boolean), automated test cases, manual test cases """ automated_test_case_list = [] manual_test_case_list = [] # default extract list to id if extract_list is None: extract_list = [Defines.TR_TC_ID] status, tp_info_list = self.tr_get_testsuite_info_in_test_plan(plan_id) # Make sure if the automation key is present in the extract list. # This step is required as we need to segregate manual and automated test cases if Defines.TR_TC_CUSTOM_AUTOMATED not in extract_list: extract_list.append(Defines.TR_TC_CUSTOM_AUTOMATED) self._log.info("Adding {} key to the extract list".format( Defines.TR_TC_CUSTOM_AUTOMATED)) if status: self._log.error("Extracted dictionary of plan {}".format(plan_id)) for run_dic in tp_info_list: # For each item(suite) in test plan, extract the run-id(key) and the config params (keys) for run_id, tp_suite_info in run_dic.items(): status, tc_list = self.tr_get_tescase_in_run( run_id, extract_list) for tc in tc_list: if tc['custom_automated']: automated_test_case_list.append(tc) else: manual_test_case_list.append(tc) else: self._log.error( "Error in extracting information from test plan {}".format( plan_id)) return status, automated_test_case_list, manual_test_case_list def get_test_plan_results(self, plan_id, automated): """ This method will get all the test cases status (results) for a test plan. This method will first find the runs of a test plan and then segregates the test to manual and automated lists :param plan_id: Plan id :param automated: Define.TR_MANUAL_TC = Manual test cases Defines.TR_AUTOMATED_TC = Automated test cases Defines.TR_ALL_TC = Manual and automated test cases :return: status ( Boolean), manual_tc ( Manual test case list) , automated_tc( Automated test case list) """ automated_tc = Defines.TR_TC_RESULTS_DICT.copy() manual_tc = Defines.TR_TC_RESULTS_DICT.copy() # Get all the test cases for the test plan # If automation is True get only automated test case status, automated_test_case_list, manual_test_case_list = self.tr_get_testcase_in_test_plan( plan_id, extract_list=[Defines.TR_TC_STATUS_ID]) # Get the results based on automated flag if status: try: if automated in Defines.TR_MANUAL_TC or automated in Defines.TR_ALL_TC: for tc in manual_test_case_list: status_id = tc[Defines.TR_TC_STATUS_ID] manual_tc[ Defines.TR_TC_STATUS_DICT[status_id]] = manual_tc[ Defines.TR_TC_STATUS_DICT[status_id]] + 1 if automated in Defines.TR_AUTOMATED_TC or automated in Defines.TR_ALL_TC: for tc in automated_test_case_list: status_id = tc[Defines.TR_TC_STATUS_ID] automated_tc[Defines.TR_TC_STATUS_DICT[ status_id]] = automated_tc[ Defines.TR_TC_STATUS_DICT[status_id]] + 1 manual_tc[Defines.TC_TOTAL] = sum(manual_tc.values()) automated_tc[Defines.TC_TOTAL] = sum(automated_tc.values()) except KeyError: # Raise the error else the result will not match with TestRail. The missing key needs to be fixed. self._log.exception( "Please check the TR_TC_STATUS_DICT parameters. Looks like these a mismatch with TestRail" ) raise KeyError( "Please check the TR_TC_STATUS_DICT parameters. Looks like these a mismatch with TestRail" ) self._log.info("Manual test case = {}".format(manual_tc)) self._log.info("Automated test case = {}".format(automated_tc)) return status, manual_tc, automated_tc def tr_add_test_case_result(self, run_id, case_id, status_id, jir_defect=None, version=None, comments=None, elapsed=None): """ This method will post result for a test case. We meed run id or the test plan and case id of the test to post results. :param run_id: (Mandatory) Run id of the test plan (Note: Suite id which starts with R example: RXXXX) :param case_id: (Mandatory) Case if of the test case (Note: Case id starts with C example: CXXXX) :param status_id: (Mandatory) One of the valid status id (1:pass,2:blocked,3:Untested,4:retest,5:failed,6:Not implemented,7:Not Testable) :param jir_defect: Any old or know jira defect for this test case :param version: Test code version ( firmware version) :param comments: Amu test comments for failure or pass :param elapsed: Time for the test. Default is 2 seconds :return: status( Boolean) , updated test case dictionary """ # Set the default values if elapsed is None: elapsed = Defines.TR_TC_RESULT_DEFAULT_EXEC_TIME test_result = { Defines.TR_TC_STATUS_ID: status_id, Defines.TR_TC_RESULT_COMMENT: comments, Defines.TR_TC_RESULT_ELAPSED: elapsed, Defines.TR_TC_RESULT_VERSION: version, Defines.TR_TC_RESULT_DEFECTS: jir_defect } # Make the api string api_str = Defines.TR_API_ADD_RESULT_FOR_CASES + "/" + str( run_id) + "/" + str(case_id) self._log.info("Adding results using API string {}".format(api_str)) # Call the API post method status, return_msg = self.tr_api_post(api_str, test_result) return status, return_msg def tr_add_result(self, run_id, params=None): """ This method will add result to individual test run ( Starts with TXXXXX) :param run_id: Test case Test run id :param params: Parameters to set like status, comments etc :return: status( Boolean) and return dictionary with updates """ if params is None: params = { Defines.TR_TC_RESULT_ELAPSED: '0', Defines.TR_TC_STATUS_ID: 5 } api_str = Defines.TR_API_ADD_RESULT + "/" + str(run_id) status, return_msg = self.tr_api_post(api_str, params) if not status: self._log.error( "Error in adding results for run_id {}".format(run_id)) return status, return_msg def add_result_for_entire_test_plan(self, test_plan_id, status_id, automated=Defines.TR_ALL_TC, comments=None, elapsed=None, version=None): """ This method will set the parameters like status, comment version etc for the entire test plan. You can use this function for resetting the entire plan before you start. Mostly useful in nightly test preparation """ if elapsed is None: elapsed = '0' return_msg = None # Collect all the test cases of a test plan status, automated_test_case_list, manual_test_case_list = self.tr_get_testcase_in_test_plan( test_plan_id, extract_list=[Defines.TR_RUN_ID]) tc_list = [] if automated in Defines.TR_AUTOMATED_TC: tc_list.append(automated_test_case_list) elif automated in Defines.TR_MANUAL_TC: tc_list.append(manual_test_case_list) else: tc_list.append(manual_test_case_list) tc_list.append(automated_test_case_list) for item in tc_list: # manual_test_case_list: for tc in item: api_str = Defines.TR_API_ADD_RESULT + "/" + str( tc[Defines.TR_RUN_ID]) param = { Defines.TR_TC_RESULT_ELAPSED: str(elapsed), Defines.TR_TC_STATUS_ID: status_id, Defines.TR_TC_RESULT_COMMENT: comments, Defines.TR_TC_RESULT_VERSION: version } status, return_msg = self.tr_api_post(api_str, param) return status, return_msg
required=True) parser.add_argument('--tpid', action='store', dest='tpid', help='TestRail test plan ID, ex. R2330 ', required=True) parser.add_argument( '--url', action='store', dest='url', help='TestRail url, default set to https://bsro.testrail.net', default='https://bsro.testrail.net') results = parser.parse_args() url = results.url client = APIClient(url) client.user = results.username client.password = results.password tpid_argv = results.tpid[1:] def get_creds(): if len(client.user) < 1: client.user = input("login: "******"[ha!] forgot something?....where's the password ?") sys.exit(0) print("[.] k, got the creds for ", client.user)
class TestRailProject(object): """TestRailProject.""" # TODO documentation def __init__(self, url, user, password, project): self.client = APIClient(base_url=url) self.client.user = user self.client.password = password self.project = self._get_project(project) def _get_project(self, project_name): projects_uri = 'get_projects' projects = self.client.send_get(uri=projects_uri) for project in projects: if project['name'] == project_name: return project return None def test_run_struct(self, name, suite_id, milestone_id, description, config_ids, include_all=True, assignedto=None, case_ids=None): struct = { 'name': name, 'suite_id': suite_id, 'milestone_id': milestone_id, 'description': description, 'include_all': include_all, 'config_ids': config_ids } if case_ids: struct['include_all'] = False struct['case_ids'] = case_ids if assignedto: struct['assignedto_id'] = self.get_user(assignedto)['id'] return struct def get_users(self): users_uri = 'get_users' return self.client.send_get(uri=users_uri) def get_user(self, user_id): user_uri = 'get_user/{user_id}'.format(user_id=user_id) return self.client.send_get(uri=user_uri) def get_user_by_name(self, name): for user in self.get_users(): if user['name'] == name: return self.get_user(user_id=user['id']) def get_configs(self): configs_uri = 'get_configs/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(configs_uri) def get_config(self, config_id): for configs in self.get_configs(): for config in configs['configs']: if config['id'] == int(config_id): return config def get_config_by_name(self, name): for config in self.get_configs(): if config['name'] == name: return config def get_priorities(self): priorities_uri = 'get_priorities' return self.client.send_get(uri=priorities_uri) def get_milestones(self): milestones_uri = 'get_milestones/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=milestones_uri) def get_milestone(self, milestone_id): milestone_uri = 'get_milestone/{milestone_id}'.format( milestone_id=milestone_id) return self.client.send_get(uri=milestone_uri) def get_milestone_by_name(self, name): for milestone in self.get_milestones(): if milestone['name'] == name: return self.get_milestone(milestone_id=milestone['id']) def get_suites(self): suites_uri = 'get_suites/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=suites_uri) def get_suite(self, suite_id): suite_uri = 'get_suite/{suite_id}'.format(suite_id=suite_id) return self.client.send_get(uri=suite_uri) def get_suite_by_name(self, name): for suite in self.get_suites(): if suite['name'] == name: return self.get_suite(suite_id=suite['id']) def get_sections(self, suite_id): sections_uri = 'get_sections/{project_id}&suite_id={suite_id}'.format( project_id=self.project['id'], suite_id=suite_id ) return self.client.send_get(sections_uri) def get_section(self, section_id): section_uri = 'get_section/{section_id}'.format(section_id=section_id) return self.client.send_get(section_uri) def get_section_by_name(self, suite_id, section_name): for section in self.get_sections(suite_id=suite_id): if section['name'] == section_name: return self.get_section(section_id=section['id']) def create_section(self, suite_id, name, parent_id=None): return self.client.send_post('add_section/' + str(self.project['id']), dict(suite_id=suite_id, name=name, parent_id=parent_id)) def delete_section(self, section_id): return self.client.send_post('delete_section/' + str(section_id), {}) def create_suite(self, name, description=None): return self.client.send_post('add_suite/' + str(self.project['id']), dict(name=name, description=description)) def get_cases(self, suite_id, section_id=None): cases_uri = 'get_cases/{project_id}&suite_id={suite_id}'.format( project_id=self.project['id'], suite_id=suite_id ) if section_id: cases_uri = '{0}§ion_id={section_id}'.format( cases_uri, section_id=section_id ) return self.client.send_get(cases_uri) def get_case(self, case_id): case_uri = 'get_case/{case_id}'.format(case_id=case_id) return self.client.send_get(case_uri) def update_case(self, case): case_uri = 'update_case/{case_id}'.format(case_id=case["id"]) return self.client.send_post(case_uri, case) def get_case_by_name(self, suite_id, name, cases=None): for case in cases or self.get_cases(suite_id): if case['title'] == name: return self.get_case(case_id=case['id']) def get_case_by_group(self, suite_id, group, cases=None): for case in cases or self.get_cases(suite_id): if case['custom_test_group'] == group: return self.get_case(case_id=case['id']) def add_case(self, section_id, case): add_case_uri = 'add_case/{section_id}'.format(section_id=section_id) return self.client.send_post(add_case_uri, case) def delete_case(self, case_id): return self.client.send_post('delete_case/' + str(case_id), None) def get_plans(self): plans_uri = 'get_plans/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(plans_uri) def get_plan(self, plan_id): plan_uri = 'get_plan/{plan_id}'.format(plan_id=plan_id) return self.client.send_get(plan_uri) def get_plans_by_milestone(self, milestone_id): plans = self.get_plans() return [self.get_plan(plan['id']) for plan in plans if plan['milestone_id'] == milestone_id] def get_plan_by_name(self, name): for plan in self.get_plans(): if plan['name'] == name: return self.get_plan(plan['id']) def add_plan(self, name, description, milestone_id, entries): add_plan_uri = 'add_plan/{project_id}'.format( project_id=self.project['id']) new_plan = { 'name': name, 'description': description, 'milestone_id': milestone_id, 'entries': entries } return self.client.send_post(add_plan_uri, new_plan) def add_plan_entry(self, plan_id, suite_id, config_ids, runs, name=None): add_plan_entry_uri = 'add_plan_entry/{plan_id}'.format(plan_id=plan_id) new_entry = { 'suite_id': suite_id, 'config_ids': config_ids, 'runs': runs } if name: new_entry['name'] = name return self.client.send_post(add_plan_entry_uri, new_entry) def delete_plan(self, plan_id): delete_plan_uri = 'delete_plan/{plan_id}'.format(plan_id=plan_id) self.client.send_post(delete_plan_uri, {}) def get_runs(self): runs_uri = 'get_runs/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=runs_uri) def get_run(self, run_id): run_uri = 'get_run/{run_id}'.format(run_id=run_id) return self.client.send_get(uri=run_uri) def get_run_by_name(self, name): for run in self.get_runs(): if run['name'] == name: return self.get_run(run_id=run['id']) def get_previous_runs(self, milestone_id, suite_id, config_id): all_runs = [] for plan in self.get_plans_by_milestone(milestone_id=milestone_id): for entry in plan['entries']: if entry['suite_id'] == suite_id: run_ids = [run for run in entry['runs'] if config_id in run['config_ids']] all_runs.extend(run_ids) return all_runs def add_run(self, new_run): add_run_uri = 'add_run/{project_id}'.format( project_id=self.project['id']) return self.client.send_post(add_run_uri, new_run) def update_run(self, name, milestone_id=None, description=None, config_ids=None, include_all=None, case_ids=None): tests_run = self.get_run(name) update_run_uri = 'update_run/{run_id}'.format(run_id=tests_run['id']) update_run = {} if milestone_id: update_run['milestone_id'] = milestone_id if description: update_run['description'] = description if include_all is not None: update_run['include_all'] = include_all is True if case_ids: update_run['case_ids'] = case_ids if config_ids: update_run['config_ids'] = config_ids return self.client.send_post(update_run_uri, update_run) def create_or_update_run(self, name, suite, milestone_id, description, config_ids, include_all=True, assignedto=None, case_ids=None): if self.get_run(name): self.update_run(name=name, milestone_id=milestone_id, description=description, config_ids=config_ids, include_all=include_all, case_ids=case_ids) else: self.add_run(self.test_run_struct(name, suite, milestone_id, description, config_ids, include_all=include_all, assignedto=assignedto, case_ids=case_ids)) def get_statuses(self): statuses_uri = 'get_statuses' return self.client.send_get(statuses_uri) def get_status(self, name): for status in self.get_statuses(): if status['name'] == name: return status def get_tests(self, run_id, status_id=None): tests_uri = 'get_tests/{run_id}'.format(run_id=run_id) if status_id: tests_uri = '{0}&status_id={1}'.format(tests_uri, ','.join(status_id)) return self.client.send_get(tests_uri) def get_test(self, test_id): test_uri = 'get_test/{test_id}'.format(test_id=test_id) return self.client.send_get(test_uri) def get_test_by_name(self, run_id, name): for test in self.get_tests(run_id): if test['title'] == name: return self.get_test(test_id=test['id']) def get_test_by_group(self, run_id, group, tests=None): for test in tests or self.get_tests(run_id): if test['custom_test_group'] == group: return self.get_test(test_id=test['id']) def get_test_by_name_and_group(self, run_id, name, group): for test in self.get_tests(run_id): if test['title'] == name and test['custom_test_group'] == group: return self.get_test(test_id=test['id']) def get_tests_by_group(self, run_id, group, tests=None): test_list = [] for test in tests or self.get_tests(run_id): if test['custom_test_group'] == group: test_list.append(self.get_test(test_id=test['id'])) return test_list def get_results_for_test(self, test_id, run_results=None): if run_results: for results in run_results: if results['test_id'] == test_id: return results results_uri = 'get_results/{test_id}'.format(test_id=test_id) return self.client.send_get(results_uri) def get_results_for_run(self, run_id): results_run_uri = 'get_results_for_run/{run_id}'.format(run_id=run_id) return self.client.send_get(results_run_uri) def get_results_for_case(self, run_id, case_id): results_case_uri = 'get_results_for_case/{run_id}/{case_id}'.format( run_id=run_id, case_id=case_id) return self.client.send_get(results_case_uri) def get_all_results_for_case(self, run_ids, case_id): all_results = [] for run_id in run_ids: try: results = self.get_results_for_case(run_id=run_id, case_id=case_id) except APIError as e: logger.error("[{0}], run_id={1}, case_id={2}" .format(e, run_id, case_id)) continue all_results.extend(results) return all_results def add_results_for_test(self, test_id, test_results): add_results_test_uri = 'add_result/{test_id}'.format(test_id=test_id) new_results = { 'status_id': self.get_status(test_results.status)['id'], 'comment': '\n'.join(filter(lambda x: x is not None, [test_results.description, test_results.url, test_results.comments])), 'elapsed': test_results.duration, 'version': test_results.version } if test_results.steps: new_results['custom_step_results'] = test_results.steps return self.client.send_post(add_results_test_uri, new_results) def add_results_for_cases(self, run_id, suite_id, tests_results): add_results_test_uri = 'add_results_for_cases/{run_id}'.format( run_id=run_id) new_results = {'results': []} tests_cases = self.get_cases(suite_id) for results in tests_results: case = self.get_case_by_group(suite_id=suite_id, group=results.group, cases=tests_cases) case_id = case['id'] new_result = { 'case_id': case_id, 'status_id': self.get_status(results.status)['id'], 'comment': '\n'.join(filter(lambda x: x is not None, [results.description, results.url, results.comments])), 'elapsed': results.duration, 'version': results.version, 'custom_launchpad_bug': results.launchpad_bug } if results.steps: custom_step_results = [] steps = case.get('custom_test_case_steps', None) if steps and len(steps) == len(results.steps): steps = zip(steps, results.steps) for s in steps: custom_step_results.append({ "content": s[0]["content"], "expected": s[0]["expected"], "actual": s[1]['actual'], "status_id": self.get_status(s[1]['status'])['id'] }) else: for s in results.steps: custom_step_results.append({ "content": s['name'], "expected": 'pass', "actual": s['actual'], "status_id": self.get_status(s['status'])['id'] }) new_result['custom_test_case_steps_results'] = \ custom_step_results new_results['results'].append(new_result) return self.client.send_post(add_results_test_uri, new_results) def add_results_for_tempest_cases(self, run_id, tests_results): add_results_test_uri = 'add_results_for_cases/{run_id}'.format( run_id=run_id) new_results = {'results': tests_results} return self.client.send_post(add_results_test_uri, new_results)
class TestRailProject(object): """TestRailProject.""" # TODO documentation def __init__(self, url, user, password, project): self.client = APIClient(base_url=url) self.client.user = user self.client.password = password self.project = self._get_project(project) def _get_project(self, project_name): projects_uri = 'get_projects' projects = self.client.send_get(uri=projects_uri) for project in projects: if project['name'] == project_name: return project return None def test_run_struct(self, name, suite_id, milestone_id, description, config_ids, include_all=True, assignedto=None, case_ids=None): struct = { 'name': name, 'suite_id': suite_id, 'milestone_id': milestone_id, 'description': description, 'include_all': include_all, 'config_ids': config_ids } if case_ids: struct['include_all'] = False struct['case_ids'] = case_ids if assignedto: struct['assignedto_id'] = self.get_user(assignedto)['id'] return struct def get_users(self): users_uri = 'get_users' return self.client.send_get(uri=users_uri) def get_user(self, user_id): user_uri = 'get_user/{user_id}'.format(user_id=user_id) return self.client.send_get(uri=user_uri) def get_user_by_name(self, name): for user in self.get_users(): if user['name'] == name: return self.get_user(user_id=user['id']) def get_configs(self): configs_uri = 'get_configs/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(configs_uri) def get_config(self, config_id): for configs in self.get_configs(): for config in configs['configs']: if config['id'] == int(config_id): return config def get_config_by_name(self, name): for config in self.get_configs(): if config['name'] == name: return config def get_priorities(self): priorities_uri = 'get_priorities' return self.client.send_get(uri=priorities_uri) def get_milestones(self): milestones_uri = 'get_milestones/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=milestones_uri) def get_milestone(self, milestone_id): milestone_uri = 'get_milestone/{milestone_id}'.format( milestone_id=milestone_id) return self.client.send_get(uri=milestone_uri) def get_milestone_by_name(self, name): for milestone in self.get_milestones(): if milestone['name'] == name: return self.get_milestone(milestone_id=milestone['id']) def get_suites(self): suites_uri = 'get_suites/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=suites_uri) def get_suite(self, suite_id): suite_uri = 'get_suite/{suite_id}'.format(suite_id=suite_id) return self.client.send_get(uri=suite_uri) def get_suite_by_name(self, name): for suite in self.get_suites(): if suite['name'] == name: return self.get_suite(suite_id=suite['id']) def get_sections(self, suite_id): sections_uri = 'get_sections/{project_id}&suite_id={suite_id}'.format( project_id=self.project['id'], suite_id=suite_id) return self.client.send_get(sections_uri) def get_section(self, section_id): section_uri = 'get_section/{section_id}'.format(section_id=section_id) return self.client.send_get(section_uri) def get_section_by_name(self, suite_id, section_name): for section in self.get_sections(suite_id=suite_id): if section['name'] == section_name: return self.get_section(section_id=section['id']) def create_section(self, suite_id, name, parent_id=None): return self.client.send_post( 'add_section/' + str(self.project['id']), dict(suite_id=suite_id, name=name, parent_id=parent_id)) def delete_section(self, section_id): return self.client.send_post('delete_section/' + str(section_id), {}) def create_suite(self, name, description=None): return self.client.send_post('add_suite/' + str(self.project['id']), dict(name=name, description=description)) def get_cases(self, suite_id, section_id=None): cases_uri = 'get_cases/{project_id}&suite_id={suite_id}'.format( project_id=self.project['id'], suite_id=suite_id) if section_id: cases_uri = '{0}§ion_id={section_id}'.format( cases_uri, section_id=section_id) return self.client.send_get(cases_uri) def get_case(self, case_id): case_uri = 'get_case/{case_id}'.format(case_id=case_id) return self.client.send_get(case_uri) def update_case(self, case): case_uri = 'update_case/{case_id}'.format(case_id=case["id"]) return self.client.send_post(case_uri, case) def get_case_by_name(self, suite_id, name, cases=None): for case in cases or self.get_cases(suite_id): if case['title'] == name: return self.get_case(case_id=case['id']) def get_case_by_group(self, suite_id, group, cases=None): for case in cases or self.get_cases(suite_id): if case['custom_test_group'] == group: return self.get_case(case_id=case['id']) def add_case(self, section_id, case): add_case_uri = 'add_case/{section_id}'.format(section_id=section_id) return self.client.send_post(add_case_uri, case) def delete_case(self, case_id): return self.client.send_post('delete_case/' + str(case_id), None) def get_plans(self): plans_uri = 'get_plans/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(plans_uri) def get_plan(self, plan_id): plan_uri = 'get_plan/{plan_id}'.format(plan_id=plan_id) return self.client.send_get(plan_uri) def get_plans_by_milestone(self, milestone_id): plans = self.get_plans() return [ self.get_plan(plan['id']) for plan in plans if plan['milestone_id'] == milestone_id ] def get_plan_by_name(self, name): for plan in self.get_plans(): if plan['name'] == name: return self.get_plan(plan['id']) def add_plan(self, name, description, milestone_id, entries): add_plan_uri = 'add_plan/{project_id}'.format( project_id=self.project['id']) new_plan = { 'name': name, 'description': description, 'milestone_id': milestone_id, 'entries': entries } return self.client.send_post(add_plan_uri, new_plan) def add_plan_entry(self, plan_id, suite_id, config_ids, runs, name=None): add_plan_entry_uri = 'add_plan_entry/{plan_id}'.format(plan_id=plan_id) new_entry = { 'suite_id': suite_id, 'config_ids': config_ids, 'runs': runs } if name: new_entry['name'] = name return self.client.send_post(add_plan_entry_uri, new_entry) def delete_plan(self, plan_id): delete_plan_uri = 'delete_plan/{plan_id}'.format(plan_id=plan_id) self.client.send_post(delete_plan_uri, {}) def get_runs(self): runs_uri = 'get_runs/{project_id}'.format( project_id=self.project['id']) return self.client.send_get(uri=runs_uri) def get_run(self, run_id): run_uri = 'get_run/{run_id}'.format(run_id=run_id) return self.client.send_get(uri=run_uri) def get_run_by_name(self, name): for run in self.get_runs(): if run['name'] == name: return self.get_run(run_id=run['id']) def get_previous_runs(self, milestone_id, suite_id, config_id): all_runs = [] for plan in self.get_plans_by_milestone(milestone_id=milestone_id): for entry in plan['entries']: if entry['suite_id'] == suite_id: run_ids = [ run for run in entry['runs'] if config_id in run['config_ids'] ] all_runs.extend(run_ids) return all_runs def add_run(self, new_run): add_run_uri = 'add_run/{project_id}'.format( project_id=self.project['id']) return self.client.send_post(add_run_uri, new_run) def update_run(self, name, milestone_id=None, description=None, config_ids=None, include_all=None, case_ids=None): tests_run = self.get_run(name) update_run_uri = 'update_run/{run_id}'.format(run_id=tests_run['id']) update_run = {} if milestone_id: update_run['milestone_id'] = milestone_id if description: update_run['description'] = description if include_all is not None: update_run['include_all'] = include_all is True if case_ids: update_run['case_ids'] = case_ids if config_ids: update_run['config_ids'] = config_ids return self.client.send_post(update_run_uri, update_run) def create_or_update_run(self, name, suite, milestone_id, description, config_ids, include_all=True, assignedto=None, case_ids=None): if self.get_run(name): self.update_run(name=name, milestone_id=milestone_id, description=description, config_ids=config_ids, include_all=include_all, case_ids=case_ids) else: self.add_run( self.test_run_struct(name, suite, milestone_id, description, config_ids, include_all=include_all, assignedto=assignedto, case_ids=case_ids)) def get_statuses(self): statuses_uri = 'get_statuses' return self.client.send_get(statuses_uri) def get_status(self, name): for status in self.get_statuses(): if status['name'] == name: return status def get_tests(self, run_id, status_id=None): tests_uri = 'get_tests/{run_id}'.format(run_id=run_id) if status_id: tests_uri = '{0}&status_id={1}'.format(tests_uri, ','.join(status_id)) return self.client.send_get(tests_uri) def get_test(self, test_id): test_uri = 'get_test/{test_id}'.format(test_id=test_id) return self.client.send_get(test_uri) def get_test_by_name(self, run_id, name): for test in self.get_tests(run_id): if test['title'] == name: return self.get_test(test_id=test['id']) def get_test_by_group(self, run_id, group, tests=None): for test in tests or self.get_tests(run_id): if test['custom_test_group'] == group: return self.get_test(test_id=test['id']) def get_test_by_name_and_group(self, run_id, name, group): for test in self.get_tests(run_id): if test['title'] == name and test['custom_test_group'] == group: return self.get_test(test_id=test['id']) def get_tests_by_group(self, run_id, group, tests=None): test_list = [] for test in tests or self.get_tests(run_id): if test['custom_test_group'] == group: test_list.append(self.get_test(test_id=test['id'])) return test_list def get_results_for_test(self, test_id, run_results=None): if run_results: for results in run_results: if results['test_id'] == test_id: return results results_uri = 'get_results/{test_id}'.format(test_id=test_id) return self.client.send_get(results_uri) def get_results_for_run(self, run_id): results_run_uri = 'get_results_for_run/{run_id}'.format(run_id=run_id) return self.client.send_get(results_run_uri) def get_results_for_case(self, run_id, case_id): results_case_uri = 'get_results_for_case/{run_id}/{case_id}'.format( run_id=run_id, case_id=case_id) return self.client.send_get(results_case_uri) def get_all_results_for_case(self, run_ids, case_id): all_results = [] for run_id in run_ids: try: results = self.get_results_for_case(run_id=run_id, case_id=case_id) except APIError as e: logger.error("[{0}], run_id={1}, case_id={2}".format( e, run_id, case_id)) continue all_results.extend(results) return all_results def add_results_for_test(self, test_id, test_results): add_results_test_uri = 'add_result/{test_id}'.format(test_id=test_id) new_results = { 'status_id': self.get_status(test_results.status)['id'], 'comment': '\n'.join( filter(lambda x: x is not None, [ test_results.description, test_results.url, test_results.comments ])), 'elapsed': test_results.duration, 'version': test_results.version } if test_results.steps: new_results['custom_step_results'] = test_results.steps return self.client.send_post(add_results_test_uri, new_results) def add_results_for_cases(self, run_id, suite_id, tests_results): add_results_test_uri = 'add_results_for_cases/{run_id}'.format( run_id=run_id) new_results = {'results': []} tests_cases = self.get_cases(suite_id) for results in tests_results: case = self.get_case_by_group(suite_id=suite_id, group=results.group, cases=tests_cases) case_id = case['id'] new_result = { 'case_id': case_id, 'status_id': self.get_status(results.status)['id'], 'comment': '\n'.join( filter( lambda x: x is not None, [results.description, results.url, results.comments])), 'elapsed': results.duration, 'version': results.version, 'custom_launchpad_bug': results.launchpad_bug } if results.steps: custom_step_results = [] steps = case.get('custom_test_case_steps', None) if steps and len(steps) == len(results.steps): steps = zip(steps, results.steps) for s in steps: custom_step_results.append({ "content": s[0]["content"], "expected": s[0]["expected"], "actual": s[1]['actual'], "status_id": self.get_status(s[1]['status'])['id'] }) else: for s in results.steps: custom_step_results.append({ "content": s['name'], "expected": 'pass', "actual": s['actual'], "status_id": self.get_status(s['status'])['id'] }) new_result['custom_test_case_steps_results'] = \ custom_step_results new_results['results'].append(new_result) return self.client.send_post(add_results_test_uri, new_results)
def get_testrail_client(): client = APIClient(TESTRAIL_URL) client.user = TESTRAIL_USER client.password = TESTRAIL_PASSWORD return client
class TestRailLibraryExt: """ Intercommunication with TestRail """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = '0.1' #__all__ = ['Add_Result_To_TestRail_Case'] def __init__(self, TestRailURL='', user='', APIkey=''): self._client = APIClient(TestRailURL) self._client.user = user self._client.password = APIkey def __str__(self): return 'RobotFramework TestRailLibrary' def Add_Result_To_TestRail_Case(self, project_name, test_run_name, caseID, status, **kwargs): runID = self.Get_TestRail_RunID(self, project_name, test_run_name) body = {} for key in kwargs: body[key] = kwargs[key] statusID = 5 if status == 'PASS': statusID = 1 body['status_id'] = statusID result = self._client.send_post( 'add_result_for_case/' + str(runID) + '/' + str(caseID), body) return result def Add_Result_To_TestRail_Case_Run_Config(self, project_name, plan_name, test_run_name, config, caseID, status, **kwargs): runID = self.Get_TestRail_RunID_by_plan_configs( self, project_name, plan_name, test_run_name, config) body = {} for key in kwargs: body[key] = kwargs[key] statusID = 5 if status == 'PASS': statusID = 1 body['status_id'] = statusID result = self._client.send_post( 'add_result_for_case/' + str(runID) + '/' + str(caseID), body) return result @staticmethod def Get_TestRail_ProjectID(self, name): projects = self._client.send_get('get_projects') for project in projects: if project['name'] == name: return project['id'] return 0 @staticmethod def Get_TestRail_ProjectRuns(self, project_name): projectID = self.Get_TestRail_ProjectID(self, project_name) runs = self._client.send_get('get_runs/' + str(projectID)) return runs @staticmethod def Get_TestRail_RunID(self, project_name, run_name): runs = self.Get_TestRail_ProjectRuns(self, project_name) for run in runs: if run['name'] == run_name: return run['id'] return 0 @staticmethod def Get_TestRail_Plans(self, project_name): projectID = self.Get_TestRail_ProjectID(self, project_name) plans = self._client.send_get('get_plans/' + str(projectID)) print(projectID) print(plans) print("I am in plans") return plans @staticmethod def Get_TestRail_PlanID(self, project_name, plan_name): plans = self.Get_TestRail_Plans(self, project_name) for plan in plans: print(plan) if plan['name'] == plan_name: return plan['id'] return 0 @staticmethod def Get_TestRail_PlanRuns(self, project_name, plan_name): planID = self.Get_TestRail_PlanID(self, project_name, plan_name) planRuns = self._client.send_get('get_plan/' + str(planID)) return planRuns['entries'] @staticmethod def Get_TestRail_RunID_by_plan_configs(self, project_name, plan_name, run_name, configs): planRuns = self.Get_TestRail_PlanRuns(self, project_name, plan_name) for run in planRuns: if configs is None: if run['name'] == run_name: return run['runs'][0]['id'] else: for subrun in run['runs']: if subrun['name'] == run_name and subrun[ 'config'] == configs: return subrun['id'] return 0 @staticmethod def Get_TestRail_configIDs_by_names(self, project_name, names): projectID = self.Get_TestRail_ProjectID(self, project_name) config_groups = self._client.send_get('get_configs/' + str(projectID)) ids = [] for group in config_groups: for config in group['configs']: if config['name'] in names: ids.append(config['id']) return ids @staticmethod def Get_TestRail_Suite(self, project_name, name): projectID = self.Get_TestRail_ProjectID(self, project_name) suites = self._client.send_get('get_suites/' + str(projectID)) print('printing suites') print(suites) for suite in suites: if suite['name'] == name: return suite return None @staticmethod def Create_Test_Run_with_all_cases(self, project_name, run_name, description): projectID = self.Get_TestRail_ProjectID(self, project_name) data = {} data['name'] = run_name data['description'] = description result = self._client.send_post('add_run/' + str(projectID), data) return result @staticmethod def Add_Test_Run_to_Plan_with_all_cases_configs(self, project_name, suite, plan_name, run_name, description, configs): planID = self.Get_TestRail_PlanID(self, project_name, plan_name) configIDs = self.Get_TestRail_configIDs_by_names( self, project_name, configs) suite = self.Get_TestRail_Suite(self, project_name, suite) run = {} plan = {} plan['suite_id'] = suite['id'] plan['name'] = run_name plan['config_ids'] = configIDs # run['name'] = run_name run['description'] = description run['include_all'] = True run['config_ids'] = configIDs # run['suite_id'] = suite['id'] plan['runs'] = [run] result = self._client.send_post('add_plan_entry/' + str(planID), plan) return result @staticmethod def Add_Test_Run_to_Plan_with_all_cases_configs_set( self, project_name, suite, plan_name, run_name, description, configs_set): allPlanRuns = self.Get_TestRail_PlanRuns(self, project_name, plan_name) for existingRun in allPlanRuns: if existingRun['name'] == run_name: return 'Test Run already created. No actions needed' planID = self.Get_TestRail_PlanID(self, project_name, plan_name) allConfigIds = [] for conf in configs_set: configIDs = self.Get_TestRail_configIDs_by_names( self, project_name, conf) allConfigIds.extend(configIDs) suite = self.Get_TestRail_Suite(self, project_name, suite) plan = {} plan['suite_id'] = suite['id'] plan['name'] = run_name plan['config_ids'] = list(set(allConfigIds)) runs = [] for conf in configs_set: run = {} confIDs = self.Get_TestRail_configIDs_by_names( self, project_name, conf) run['description'] = description run['include_all'] = True run['config_ids'] = confIDs runs.append(run) plan['runs'] = runs pprint.pprint(plan) result = self._client.send_post('add_plan_entry/' + str(planID), plan) return result def Pretty_Print_Test_Comment(self, header='EXECUTION INFO', **kwargs): comment = header + '\n\n' for key in kwargs: val = '' if isinstance(kwargs[key], list): val = ', '.join(str(x) for x in kwargs[key]) else: val = kwargs[key] comment = comment + str(key) + ": " + val + '\n\n' return comment def Extract_CaseID_From_Robot_Tags(self, tags): for tag in tags: st = str(tag) match = re.findall(r'^C(\d{0,}\d$)', st) if len(match) > 0: return int(match[0]) else: return 0 return 0 def get_case(self, caseID): case = self._client.send_get('get_case/' + str(caseID)) return case
def __init__(self, TestRailURL='', user='', APIkey=''): self._client = APIClient(TestRailURL) self._client.user = user self._client.password = APIkey
class TestRailListener(object): """ Fixing of testing results and update test case in [ http://www.gurock.com/testrail/ | TestRail ].\n == Dependency == - [ http://docs.gurock.com/testrail-api2/bindings-python | TestRail API2 python binding] == Preconditions == 1. [ http://docs.gurock.com/testrail-api2/introduction | Enable TestRail API] \n 2. Create custom field "case_description" with type "text", which corresponds to the Robot Framework's test case documentation. == Example == 1. Create test case in TestRail with case_id = 10\n 2. Add it to test run with id run_id = 20\n 3. Create autotest in Robot Framework | *** Settings *** | *** Test Cases *** | Autotest name | [Documentation] Autotest documentation | [Tags] testrailid=10 defects=BUG-1, BUG-2 references=REF-3, REF-4 | Fail Test fail message 4. Run Robot Framework with listener:\n | set ROBOT_SYSLOG_FILE=syslog.txt | pybot.bat --listener TestRailListener.py:testrail_server_name:tester_user_name:tester_user_password:20:update autotest.txt 5. Test with case_id=10 will be marked as failed in TestRail with message "Test fail message" and defects "BUG-1, BUG-2". Also title, description and references of this test will be updated in TestRail. Parameter "update" is optional. """ ROBOT_LISTENER_API_VERSION = 3 def __init__(self, server, user, password, run_id, update = None): """ *Args:*\n _server_ - the name of TestRail server; _user_ - the name of TestRail user; _password_ - the password of TestRail user; _run_id - the ID of the test run; _update_ - indicator to update test case in TestRail; if exist, then test will be updated. """ testrail_url = 'http://' + server + '/testrail/' self.client = APIClient(testrail_url) self.client.user = user self.client.password = password self.run_id = run_id self.update = update self.results = list() logger.info('[TestRailListener] url: ' + testrail_url) logger.info('[TestRailListener] user: '******'[TestRailListener] password: '******'[TestRailListener] the ID of the test run: ' + run_id) def end_test(self, name, attrs): """ Update test case in TestRail *Args:*\n _name_ - the name of test case in Robot Framework\n _attrs_ - attributes of test case in Robot Framework """ tags_value = self._getTagsValue (attrs['tags']) case_id = tags_value['testrailid'] defects = tags_value['defects'] references = tags_value['references'] if attrs['status'] == 'PASS': status_id = 1 else : status_id = 5 if case_id: # Add results to list test_result = { 'case_id': case_id, 'status_id': status_id, 'comment': attrs['message'], 'defects': defects } self.results.append(test_result) # Update test case if self.update: logger.info ('[TestRailListener] update of test ' + case_id + ' in TestRail') description = attrs['doc'] + '\n' + 'Path to test: ' + attrs['longname'] result = self.client.send_post( 'update_case/' + case_id, { 'title': name, 'type_id': 1, 'custom_case_description': description, 'refs': references } ) logger.info ( '[TestRailListener] result for method update_case ' + json.dumps(result, sort_keys=True, indent=4) ) tag = 'testRailId={case_id}'.format(case_id=case_id) logger.info('[TestRailListener] remove tag {tag} from test case report'.format(tag=tag)) BuiltIn().run_keyword('remove tags', tag) def close (self): """ Save test results for all tests in TestRail """ self.client.send_post('add_results_for_cases/' + self.run_id, {'results': self.results}) def _getTagsValue (self, tags): """ Get value from robot framework's tags for TestRail. """ attributes = dict() matchers = ['testrailid', 'defects', 'references'] for matcher in matchers: for tag in tags: match = re.match(matcher, tag) if match: split_tag = tag.split('=') tag_value = split_tag[1] attributes[matcher] = tag_value break else: attributes[matcher] = None return attributes
def main(): server = "http://SERVER.COM" login = "******" password = "******" START_DATE = "01/08/2018" END_DATE = "31/08/2018" TESTERS = ['USER1EMAIL', 'USER2EMAIL'] start_date = mktime(datetime.strptime(START_DATE, "%d/%m/%Y").timetuple()) end_date = mktime(datetime.strptime(END_DATE, "%d/%m/%Y").timetuple()) client = APIClient(server) client.user = login client.password = password print "Getting testers... " testers = {} for tester_email in TESTERS: try: tester = client.send_get('get_user_by_email&email={}'.format(tester_email)) testers.update({ tester["id"]: { 'email': tester_email, 'created': 0, 'updated': 0 } }) except APIError: print "Tester's email is not valid", tester_email projects = client.send_get('get_projects/') for project in projects: print "=" * 80 print "PROJECT", project["id"] project_id = project["id"] project_suites = client.send_get('get_suites/{}'.format(project_id)) for suite in project_suites: print "-" * 80 print "SUITE", suite["id"] suite_id = suite["id"] created = client.send_get( 'get_cases/{0}&suite_id={1}&created_after={2}&created_before={3}'.format( project_id, suite_id, int(start_date), int(end_date))) updated = client.send_get( 'get_cases/{0}&suite_id={1}&updated_after={2}&updated_before={3}'.format( project_id, suite_id, int(start_date), int(end_date))) for c in created: tid = c['created_by'] if tid in testers: testers[tid]['created'] += 1 # import pdb; pdb.set_trace() for c in updated: tid = c['updated_by'] if tid in testers: testers[tid]['updated'] += 1 # ------------------------------------------------------------------------ print "=" * 80 pprint(testers)
from creds import * from testrail import APIClient import sys import datetime client = APIClient(TESTRAIL_URL) client.user = TESTRAIL_USER client.password = TESTRAIL_API_TOKEN RUN_ID = 371 project_id = 0 status_styles = {} def make_report(test_run_id): indent = 1 html_output = '' # Import structured header info with open('html_headers.html', 'r') as head_file: html_output += head_file.read() # Add body of html details html_output += get_run_info(test_run_id, indent + 1) # Add closing html elements html_output += close_element('div', indent) html_output += close_element('body', 0) html_output += close_element('html', 0)
from pprint import pprint import json from testrail import APIClient, APIError import requests client = APIClient('') client.user = '' client.password = '' class TestRailClient: def add_result(self, test_id, status: str): _ = client.send_post(f'add_result/{test_id}', {"status_id": 5}) def add_result_for_case(self, run_id: int, case_id: int, status: str, msg: str): """Add test case and update result """ if status.lower() == "pass": result = client.send_post( 'add_result_for_case/%d/%d' % (run_id, case_id), { 'status_id': 1, 'comment': msg }) return result elif status.lower() == "fail": result = client.send_post( 'add_result_for_case/%d/%d' % (run_id, case_id), { 'status_id': 5,
def __init__(self, url, user, password, project): self.client = APIClient(base_url=url) self.client.user = user self.client.password = password self.project = self._get_project(project)
class TestRailListener(object): """ Fixing of testing results and update test case in [ http://www.gurock.com/testrail/ | TestRail ].\n == Dependency == - [ http://docs.gurock.com/testrail-api2/bindings-python | TestRail API2 python binding] == Preconditions == 1. [ http://docs.gurock.com/testrail-api2/introduction | Enable TestRail API] \n 2. Create custom field "case_description" with type "text", which corresponds to the Robot Framework's test case documentation. == Example == 1. Create test case in TestRail with case_id = 10\n 2. Add it to test run with id run_id = 20\n 3. Create autotest in Robot Framework | *** Settings *** | *** Test Cases *** | Autotest name | [Documentation] Autotest documentation | [Tags] testrailid=10 defects=BUG-1, BUG-2 references=REF-3, REF-4 | Fail Test fail message 4. Run Robot Framework with listener:\n | set ROBOT_SYSLOG_FILE=syslog.txt | pybot.bat --listener TestRailListener.py:testrail_server_name:tester_user_name:tester_user_password:20:update autotest.txt 5. Test with case_id=10 will be marked as failed in TestRail with message "Test fail message" and defects "BUG-1, BUG-2". Also title, description and references of this test will be updated in TestRail. Parameter "update" is optional. """ ROBOT_LISTENER_API_VERSION = 2 def __init__(self, server, user, password, run_id, update = None): """ *Args:*\n _server_ - the name of TestRail server; _user_ - the name of TestRail user; _password_ - the password of TestRail user; _run_id - the ID of the test run; _update_ - indicator to update test case in TestRail; if exist, then test will be updated. """ testrail_url = 'http://' + server + '/testrail/' self.client = APIClient(testrail_url) self.client.user = user self.client.password = password self.run_id = run_id self.update = update self.results = list() logger.info('[TestRailListener] url: ' + testrail_url) logger.info('[TestRailListener] user: '******'[TestRailListener] password: '******'[TestRailListener] the ID of the test run: ' + run_id) def end_test(self, name, attrs): """ Update test case in TestRail *Args:*\n _name_ - the name of test case in Robot Framework\n _attrs_ - attributes of test case in Robot Framework """ tags_value = self._getTagsValue (attrs['tags']) case_id = tags_value['testrailid'] defects = tags_value['defects'] references = tags_value['references'] if attrs['status'] == 'PASS': status_id = 1 else : status_id = 5 if case_id: # Add results to list test_result = {'case_id': case_id, 'status_id': status_id, 'comment': attrs['message'], 'defects': defects} self.results.append(test_result) # Update test case if self.update: logger.info ('[TestRailListener] update of test ' + case_id + ' in TestRail') description = attrs['doc'] + '\n' + 'Path to test: ' + attrs['longname'] result = self.client.send_post( 'update_case/' + case_id, { 'title': name, 'type_id': 1, 'custom_case_description': description, 'refs': references} ) logger.info ('[TestRailListener] result for method update_case ' + json.dumps(result, sort_keys = True, indent = 4)) def close (self): """ Save test results for all tests in TestRail """ self.client.send_post('add_results_for_cases/' + self.run_id, { 'results':self.results }) def _getTagsValue (self, tags): """ Get value from robot framework's tags for TestRail. """ attributes = dict() matchers = ['testrailid', 'defects', 'references'] for matcher in matchers : for tag in tags: match = re.match(matcher, tag) if match : split_tag = tag.split('=') tag_value = split_tag[1] attributes [matcher] = tag_value break else: attributes [matcher] = None return attributes
class TestRailTagger(object): """ class to register robot's test cases in test rail and then add corresponding tags to robot's test cases """ def __init__(self, host, user, password, file_path, section_id, file_format='robot'): """ :param host: test rail host :param user: user name :param password: password :param file_path: path of test case files or directory :param section_id: section to store auto test cases :param file_format: default to be .robot """ testrail_url = 'http://' + host + '/testrail/' self.client = APIClient(testrail_url, user, password) self.file = list() # to loop through the test suites try: if os.path.isdir(file_path): for files in os.walk(file_path): for robot_file in filter(lambda x: x.endswith('.robot'), files[2]): self.file.append( TestData(source=os.path.abspath( os.path.join(files[0], robot_file)))) else: self.file.append(TestData(source=file_path)) except DataError as e: # .robot file may have no test cases in it logging.warn('[TestRailTagger]' + e.message) self.section = section_id self.writer = DataFileWriter(format=file_format) def add_tag(self): """ add specific tags to test cases """ for suite in self.file: # to handle force tags, delete force tags in setting table # and then add the tag value to each test case in a suite force_tags = suite.setting_table.force_tags.value suite.setting_table.force_tags.value = None for test in suite.testcase_table.tests: getattr(self, TAG_TYPE['testRail'])(test, suite, force_tags) def add_test_rail_id(self, test, test_case_file, other_tags): """ register test case and add the test case id to .robot file :param test: TestData class object, one of test cases read from goven .robot file :param test_case_file: the .robot file to write (add tags) :param other_tags: param for the force tags(also for other tags) """ if test.tags.value is None: test.tags.value = list() tags_value = getTagsValue(test.tags.value) case_id = tags_value.get('testRailId') if case_id is None: res = self.client.send_post( 'add_case/{section_id}'.format(section_id=self.section), { 'title': 'Auto-' + test.name, 'type_id': 1, 'priority_id': 3, 'estimate': '1h', 'refs': '', 'custom_steps': [{ 'content': getattr(step, 'name', ''), 'expected': 'auto-results' } for step in test.steps if step] }) case_id = res.get('id') logging.info( '[TestRailTagger] register test {case_id} to TestRail'.format( case_id=case_id)) test.tags.value.append( TEST_RAIL_ID_TEMPLATE.format(case_id=case_id)) test.tags.value += other_tags self.writer.write(test_case_file)
import sys import getpass import json import pprint from testrail import APIClient url = 'https://bsro.testrail.net' client = APIClient(url) client.user = '******' client.password = sys.argv[1] #def usage(): # print("[!] Usage: tr_conn.py -p password") # for eachArg in sys.argv: # print(eachArg) def get_creds(): print("[<] Enter TestRail login:\n") if len(client.user) < 1: client.user = input("login: "******"[ha!] forgot something?....where's the password ?") sys.exit(0) print("[.] k, got the creds for ", client.user) def input_yes_no(question, default="yes"):
def testrail_requester(): config = load_configuration() client = APIClient("https://dromeroa.testrail.io") client.user = "******" client.password = config["TESTRAIL_TOKEN"] return client