Package: octogaming-apiist
ApiistApiist is anApiritif forks. It also aims to simplify python api testing with a set of
predefined assertions and request methods. The fork was motivated by the project lacks of apparence maintenance and the need to
support testing using Async methods and Starlette test client related to a FastAPI usage.Install Apiist:pip install octogaming-apiistCheck Apiist version with the following command:python -m octogaming-apiist -- versionHere described some features of Apiist which can help you to create tests more easily.OverviewHTTP RequestsApiist allows to use simplerequests-like API for making HTTP requests.fromapiistimporthttpresponse=http.get("http://example.com")response.assert_ok()# will raise AssertionError if request wasn't successfulhttpobject provides the following methods:fromapiistimporthttphttp.get("http://api.example.com/posts")http.post("http://api.example.com/posts")http.put("http://api.example.com/posts/1")http.patch("http://api.example.com/posts/1")http.delete("http://api.example.com/posts/1")http.head("http://api.example.com/posts")All methods (get,post,put,patch,delete,head) support the following arguments:defget(address,# URL for the requestparams=None,# URL params dictheaders=None,# HTTP headerscookies=None,# request cookiesdata=None,# raw request datajson=None,# attach JSON object as request bodyencrypted_cert=None,# certificate to use with requestallow_redirects=True,# automatically follow HTTP redirectstimeout=30)# request timeout, by default it's 30 secondsCertificate usageCurrentlyhttpsupportspemandpkcs12certificates.
Here is an example of certificate usage:http.get("http://api.example.com/posts",encrypted_cert=('./cert.pem','passphrase'))First parameter is path to certificate, second is the passphrase certificate encrypted with.HTTP TargetsTarget is an object that captures resource name of the URL (protocol, domain, port)
and allows to set some settings applied to all requests made for a target.fromapiistimporthttpqa_env=http.target("http://192.160.0.2")qa_env.get("/api/v4/user")qa_env.get("/api/v4/user")Target constructor supports the following options:target=apiist.http.target(address,# target base addressbase_path=None,# base path prepended to all paths (e.g. '/api/v2')use_cookies=True,# use cookiesadditional_headers=None,# additional headers for all requestskeep_alive=True,# reuse opened HTTP connectionauto_assert_ok=True,# automatically invoke 'assert_ok' after each request)AssertionsApiist responses provide a lot of useful assertions that can be used on responses.Here's the list of assertions that can be used:response=http.get("http://example.com/")# assert that request succeeded (status code is 2xx or 3xx)response.assert_ok()# assert that request has failedresponse.assert_failed()# status code based assertionsresponse.assert_2xx()response.assert_3xx()response.assert_4xx()response.assert_5xx()response.assert_status_code(code)response.assert_not_status_code(code)response.assert_status_code_in(codes)# content-based assertions# assert that response body contains a stringresponse.assert_in_body(member)# assert that response body doesn't contain a stringresponse.assert_not_in_body(member)# search (or match) response body with a regexresponse.assert_regex_in_body(regex,match=False)response.assert_regex_not_in_body(regex,match=False)# assert that response has headerresponse.assert_has_header(header)# assert that response has header with given valueresponse.assert_header_value(header,value)# assert that response's headers contains a stringresponse.assert_in_headers(member)response.assert_not_in_headers(member)# search (or match) response body with a regexresponse.assert_regex_in_headers(member)response.assert_regex_not_in_headers(member)# assert that response body matches JSONPath queryresponse.assert_jsonpath(jsonpath_query,expected_value=None)response.assert_not_jsonpath(jsonpath_query)# assert that response body matches XPath queryresponse.assert_xpath(xpath_query,parser_type='html',validate=False)response.assert_not_xpath(xpath_query,parser_type='html',validate=False)# assert that HTML response body contains CSS selector itemresponse.assert_cssselect(selector,expected_value=None,attribute=None)response.assert_not_cssselect(selector,expected_value=None,attribute=None)Note that assertions can be chained, so the following construction is entirely valid:response=http.get("http://example.com/")response.assert_ok().assert_in_body("Example")TransactionsApiist allows to group multiple requests or actions into a transaction using atransactioncontext manager.
For example when we have test action like bellow we want to execute requests according to concrete user as a separate piece.
Also we want to process test forusers/allpage even if something wrong with previous actions.deftest_with_login():user_credentials=data_mock.get_my_user()http.get("https://blazedemo.com/user/login?id="+user_credentials.id).assert_ok()http.get("https://blazedemo.com/user/id/personalPage").assert_ok()http.get("https://blazedemo.com/user/id/getPersonalData").assert_ok()http.get("https://blazedemo.com/users/all").assert_ok()Here where we can use transaction in order to wrap login process in one block.deftest_with_login():withapiist.transaction('Login'):user_credentials=data_mock.get_my_user()http.get("https://blazedemo.com/user/login?id="+user_credentials.id).assert_ok()http.get("https://blazedemo.com/user/id/personalPage").assert_ok()http.get("https://blazedemo.com/user/id/getPersonalData").assert_ok()http.get("https://blazedemo.com/users/all").assert_ok()At the same time requests tousers/allpage will be executed outside of transaction even if something inside transaction fails.Transaction defines the name for the block of code. This name with execution results of this particular block will be displayed in the output report.Smart transactionssmart_transactionis advanced option for test flow control (stop or continue after failed test method).
Let see another test method example:classTests(TestCase):deftest_available_pages():http.get("https://blazedemo.com/").assert_ok()http.get("https://blazedemo.com/users").assert_ok()http.get("https://blazedemo.com/users/search").assert_ok()http.get("https://blazedemo.com/users/count").assert_ok()http.get("https://blazedemo.com/users/login").assert_ok()http.get("https://blazedemo.com/contactUs").assert_ok()http.get("https://blazedemo.com/copyright").assert_ok()In this case we have multiple requests divided into blocks. I do not want to test pages underusersspace if it is not available.
For this purpose we can usesmart_transaction.classTests(TestCase):defsetUp(self):apiist.put_into_thread_store(func_mode=True)deftest_available_pages():http.get("https://blazedemo.com/").assert_ok()withapiist.smart_transaction('Availability check'):http.get("https://blazedemo.com/users").assert_ok()withapiist.smart_transaction('Test users pages'):http.get("https://blazedemo.com/users/search").assert_ok()http.get("https://blazedemo.com/users/count").assert_ok()http.get("https://blazedemo.com/users/login").assert_ok()http.get("https://blazedemo.com/contactUs").assert_ok()http.get("https://blazedemo.com/copyright").assert_ok()Now this two blocks are wrapped intosmart_transactionwhich would help with error test flow handling and logging.Also each transaction defines the name for the block of code and will be displayed in the output report.Now aboutapiist.put_into_thread_store(func_mode=True), this is test execution mode for apiist.
We can execute all of the transactions in test no matter what or stop after first failed transaction.
This flag tells to apiist "Stop execution if some transaction failed".Falsesays "Run till the end in any case".Nose Flow ControlIt's one more feature based on smart transactions. It changesfunc_modeif necessary to execute whole teardown block,
intended to finalize all necessary things.deftest_flow-control(self):try:self._method_with_exception()self._skipped_method()finally:apiist.set_stage("teardown")self._teardown1()self._teardown2()If this test will be interrupted in_method_with_exception, both of teardown methods will be executed even if them raise exception.
Please note two differences with usage oftearDownmethod of nose:all parts of teardown stage will be executed as mentioned above (will be interrupted in regular nose execution)results of teardown steps will be written by apiist SampleWriter into output file (nose lost them as tearDown isn't recognised as test).Graceful shutdownSomethimes waiting of end of test isn't necessary and we prefer to break it but save all current results and handle all teardown steps. (see above)
It's possible with GRACEFUL flag. To use it you can run apiist with GRACEFUL environment variable pointed to any file name.
Apiist will be interrupted as soon as the file is created.CSV ReaderIn order to use data from csv file as test parameters Apiist provides two different csv readers.
SimpleCSVReaderhelps you to read data from file line by line and use this data wherever you need:data_reader=apiist.CSVReader('---path to required file---')classTests(TestCase):deftest_user_page():data_reader.read_vars()vars=data_reader.get_vars()http.get("https://blazedemo.com/users/"+vars.user_id).assert_ok()In case of multithreading testing you may need to deviate data between threads and ysu uniq lines for each thread.CSVReaderPerThreadhelps to solve this problem:data_per_thread_reader=apiist.CSVReaderPerThread('---path to required file---')classTests(TestCase):defsetUp(self):data_per_thread_reader.read_vars()self.vars=data_per_thread_reader.get_vars()deftest_user_page():http.get("https://blazedemo.com/users/"+self.vars.user_id).assert_ok()Execution resultsApiist writes output data from tests inapiist.#.csvfiles by default. Here#is number of executing process.
The output file is similar to this:timeStamp,elapsed,Latency,label,responseCode,responseMessage,success,allThreads,bytes
1602759519185,0,0,Correct test,,,true,0,2
1602759519186,0,0,Correct transaction,,,true,0,2
1602759519187,0,0,Test with exception,,Exception: Horrible error,false,0,2It contains test and transaction results for executed tests by one process.Environment VariablesThere are environment variables to control length of response/request body to be written into traces and logs:APIIST_TRACE_BODY_EXCLIMIT- limit of body part to include into exception messages, default is 1024APIIST_TRACE_BODY_HARDLIMIT- limit of body length to include into JSON trace records, default is unlimited