diff options
Diffstat (limited to 'python-apiritif.spec')
-rw-r--r-- | python-apiritif.spec | 997 |
1 files changed, 997 insertions, 0 deletions
diff --git a/python-apiritif.spec b/python-apiritif.spec new file mode 100644 index 0000000..399b3cd --- /dev/null +++ b/python-apiritif.spec @@ -0,0 +1,997 @@ +%global _empty_manifest_terminate_build 0 +Name: python-apiritif +Version: 1.1.3 +Release: 1 +Summary: Python framework for API testing +License: Apache 2.0 +URL: https://github.com/Blazemeter/apiritif +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/3f/49/1b639130c415badb18aa4cceb9f6ae0af80c7c528b247cc2542b9b746838/apiritif-1.1.3.tar.gz +BuildArch: noarch + +Requires: python3-nose2 +Requires: python3-pytest +Requires: python3-requests +Requires: python3-jsonpath-ng +Requires: python3-lxml +Requires: python3-unicodecsv +Requires: python3-cssselect +Requires: python3-chardet +Requires: python3-pyopenssl +Requires: python3-codecov + +%description +# Apiritif + +Apiritif is a number of utilities aimed to simplify the process of maintaining API tests. +Apiritif tests fully based on python nose tests. This library can help you to develop and run your existing tests. +In order to create any valid tests for Apiritif you can read [nose test documentation](https://nose.readthedocs.io/en/latest/testing.html). + +Check Apiritif version with the following command: +``` +python -m apiritif -- version +``` + +Here described some features of Apiritif which can help you to create tests more easily. + +## Overview + +## HTTP Requests + +Apiritif allows to use simple `requests`-like API for making HTTP requests. + +```python +from apiritif import http + +response = http.get("http://example.com") +response.assert_ok() # will raise AssertionError if request wasn't successful +``` + +`http` object provides the following methods: +```python +from apiritif import http + +http.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: +```python +def get(address, # URL for the request + params=None, # URL params dict + headers=None, # HTTP headers + cookies=None, # request cookies + data=None, # raw request data + json=None, # attach JSON object as request body + encrypted_cert=None, # certificate to use with request + allow_redirects=True, # automatically follow HTTP redirects + timeout=30) # request timeout, by default it's 30 seconds +``` + +##### Certificate usage +Currently `http` supports `pem` and `pkcs12` certificates. +Here is an example of certificate usage: +```python +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 Targets + +Target 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. + + +```python +from apiritif import http + +qa_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: +```python +target = apiritif.http.target( + address, # target base address + base_path=None, # base path prepended to all paths (e.g. '/api/v2') + use_cookies=True, # use cookies + additional_headers=None, # additional headers for all requests + keep_alive=True, # reuse opened HTTP connection + auto_assert_ok=True, # automatically invoke 'assert_ok' after each request +) +``` + + +## Assertions + +Apiritif responses provide a lot of useful assertions that can be used on responses. + +Here's the list of assertions that can be used: +```python +response = http.get("http://example.com/") + +# assert that request succeeded (status code is 2xx or 3xx) +response.assert_ok() +# assert that request has failed +response.assert_failed() + +# status code based assertions +response.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 string +response.assert_in_body(member) + +# assert that response body doesn't contain a string +response.assert_not_in_body(member) + +# search (or match) response body with a regex +response.assert_regex_in_body(regex, match=False) +response.assert_regex_not_in_body(regex, match=False) + +# assert that response has header +response.assert_has_header(header) + +# assert that response has header with given value +response.assert_header_value(header, value) + +# assert that response's headers contains a string +response.assert_in_headers(member) +response.assert_not_in_headers(member) + +# search (or match) response body with a regex +response.assert_regex_in_headers(member) +response.assert_regex_not_in_headers(member) + +# assert that response body matches JSONPath query +response.assert_jsonpath(jsonpath_query, expected_value=None) +response.assert_not_jsonpath(jsonpath_query) + +# assert that response body matches XPath query +response.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 item +response.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: +```python + +response = http.get("http://example.com/") +response.assert_ok().assert_in_body("Example") +``` + +## Transactions + +Apiritif allows to group multiple requests or actions into a transaction using a `transaction` context 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 for `users/all` page even if something wrong with previous actions. + +```python +def test_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. + +```python +def test_with_login(): + with apiritif.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 to `users/all` page 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 transactions + +`smart_transaction` is advanced option for test flow control (stop or continue after failed test method). +Let see another test method example: + +```python +class Tests(TestCase): + def test_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 under `users` space if it is not available. +For this purpose we can use `smart_transaction`. + +```python +class Tests(TestCase): + def setUp(self): + apiritif.put_into_thread_store(func_mode=True) + + def test_available_pages(): + http.get("https://blazedemo.com/").assert_ok() + + with apiritif.smart_transaction('Availability check'): + http.get("https://blazedemo.com/users").assert_ok() + + with apiritif.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 into `smart_transaction` which 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 about `apiritif.put_into_thread_store(func_mode=True)`, this is test execution mode for apiritif. +We can execute all of the transactions in test no matter what or stop after first failed transaction. +This flag tells to apiritif "Stop execution if some transaction failed". `False` says "Run till the end in any case". + +##### Nose Flow Control +It's one more feature based on smart transactions. It changes `func_mode` if necessary to execute whole teardown block, +intended to finalize all necessary things. + +```python +def test_flow-control(self): + try: + self._method_with_exception() + self._skipped_method() + finally: + apiritif.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 of `tearDown` method of nose: +1. all parts of teardown stage will be executed as mentioned above (will be interrupted in regular nose execution) +2. results of teardown steps will be written by apiritif SampleWriter into output file (nose lost them as tearDown isn't recognised as test). + +##### Graceful shutdown +Somethimes 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 apiritif with GRACEFUL environment variable pointed to any file name. +Apiritif will be interrupted as soon as the file is created. + +## CSV Reader +In order to use data from csv file as test parameters Apiritif provides two different csv readers. +Simple `CSVReader` helps you to read data from file line by line and use this data wherever you need: + +```python +data_reader = apiritif.CSVReader('---path to required file---') +class Tests(TestCase): + def test_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. +`CSVReaderPerThread` helps to solve this problem: + +```python +data_per_thread_reader = apiritif.CSVReaderPerThread('---path to required file---') +class Tests(TestCase): + def setUp(self): + data_per_thread_reader.read_vars() + self.vars = data_per_thread_reader.get_vars() + + def test_user_page(): + http.get("https://blazedemo.com/users/" + self.vars.user_id).assert_ok() +``` + +## Execution results + +Apiritif writes output data from tests in `apiritif.#.csv` files by default. Here `#` is number of executing process. +The output file is similar to this: +```csv +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,2 +``` +It contains test and transaction results for executed tests by one process. + +### Environment Variables + +There are environment variables to control length of response/request body to be written into traces and logs: + * `APIRITIF_TRACE_BODY_EXCLIMIT` - limit of body part to include into exception messages, default is 1024 + * `APIRITIF_TRACE_BODY_HARDLIMIT` - limit of body length to include into JSON trace records, default is unlimited + + + + +%package -n python3-apiritif +Summary: Python framework for API testing +Provides: python-apiritif +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-apiritif +# Apiritif + +Apiritif is a number of utilities aimed to simplify the process of maintaining API tests. +Apiritif tests fully based on python nose tests. This library can help you to develop and run your existing tests. +In order to create any valid tests for Apiritif you can read [nose test documentation](https://nose.readthedocs.io/en/latest/testing.html). + +Check Apiritif version with the following command: +``` +python -m apiritif -- version +``` + +Here described some features of Apiritif which can help you to create tests more easily. + +## Overview + +## HTTP Requests + +Apiritif allows to use simple `requests`-like API for making HTTP requests. + +```python +from apiritif import http + +response = http.get("http://example.com") +response.assert_ok() # will raise AssertionError if request wasn't successful +``` + +`http` object provides the following methods: +```python +from apiritif import http + +http.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: +```python +def get(address, # URL for the request + params=None, # URL params dict + headers=None, # HTTP headers + cookies=None, # request cookies + data=None, # raw request data + json=None, # attach JSON object as request body + encrypted_cert=None, # certificate to use with request + allow_redirects=True, # automatically follow HTTP redirects + timeout=30) # request timeout, by default it's 30 seconds +``` + +##### Certificate usage +Currently `http` supports `pem` and `pkcs12` certificates. +Here is an example of certificate usage: +```python +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 Targets + +Target 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. + + +```python +from apiritif import http + +qa_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: +```python +target = apiritif.http.target( + address, # target base address + base_path=None, # base path prepended to all paths (e.g. '/api/v2') + use_cookies=True, # use cookies + additional_headers=None, # additional headers for all requests + keep_alive=True, # reuse opened HTTP connection + auto_assert_ok=True, # automatically invoke 'assert_ok' after each request +) +``` + + +## Assertions + +Apiritif responses provide a lot of useful assertions that can be used on responses. + +Here's the list of assertions that can be used: +```python +response = http.get("http://example.com/") + +# assert that request succeeded (status code is 2xx or 3xx) +response.assert_ok() +# assert that request has failed +response.assert_failed() + +# status code based assertions +response.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 string +response.assert_in_body(member) + +# assert that response body doesn't contain a string +response.assert_not_in_body(member) + +# search (or match) response body with a regex +response.assert_regex_in_body(regex, match=False) +response.assert_regex_not_in_body(regex, match=False) + +# assert that response has header +response.assert_has_header(header) + +# assert that response has header with given value +response.assert_header_value(header, value) + +# assert that response's headers contains a string +response.assert_in_headers(member) +response.assert_not_in_headers(member) + +# search (or match) response body with a regex +response.assert_regex_in_headers(member) +response.assert_regex_not_in_headers(member) + +# assert that response body matches JSONPath query +response.assert_jsonpath(jsonpath_query, expected_value=None) +response.assert_not_jsonpath(jsonpath_query) + +# assert that response body matches XPath query +response.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 item +response.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: +```python + +response = http.get("http://example.com/") +response.assert_ok().assert_in_body("Example") +``` + +## Transactions + +Apiritif allows to group multiple requests or actions into a transaction using a `transaction` context 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 for `users/all` page even if something wrong with previous actions. + +```python +def test_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. + +```python +def test_with_login(): + with apiritif.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 to `users/all` page 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 transactions + +`smart_transaction` is advanced option for test flow control (stop or continue after failed test method). +Let see another test method example: + +```python +class Tests(TestCase): + def test_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 under `users` space if it is not available. +For this purpose we can use `smart_transaction`. + +```python +class Tests(TestCase): + def setUp(self): + apiritif.put_into_thread_store(func_mode=True) + + def test_available_pages(): + http.get("https://blazedemo.com/").assert_ok() + + with apiritif.smart_transaction('Availability check'): + http.get("https://blazedemo.com/users").assert_ok() + + with apiritif.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 into `smart_transaction` which 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 about `apiritif.put_into_thread_store(func_mode=True)`, this is test execution mode for apiritif. +We can execute all of the transactions in test no matter what or stop after first failed transaction. +This flag tells to apiritif "Stop execution if some transaction failed". `False` says "Run till the end in any case". + +##### Nose Flow Control +It's one more feature based on smart transactions. It changes `func_mode` if necessary to execute whole teardown block, +intended to finalize all necessary things. + +```python +def test_flow-control(self): + try: + self._method_with_exception() + self._skipped_method() + finally: + apiritif.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 of `tearDown` method of nose: +1. all parts of teardown stage will be executed as mentioned above (will be interrupted in regular nose execution) +2. results of teardown steps will be written by apiritif SampleWriter into output file (nose lost them as tearDown isn't recognised as test). + +##### Graceful shutdown +Somethimes 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 apiritif with GRACEFUL environment variable pointed to any file name. +Apiritif will be interrupted as soon as the file is created. + +## CSV Reader +In order to use data from csv file as test parameters Apiritif provides two different csv readers. +Simple `CSVReader` helps you to read data from file line by line and use this data wherever you need: + +```python +data_reader = apiritif.CSVReader('---path to required file---') +class Tests(TestCase): + def test_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. +`CSVReaderPerThread` helps to solve this problem: + +```python +data_per_thread_reader = apiritif.CSVReaderPerThread('---path to required file---') +class Tests(TestCase): + def setUp(self): + data_per_thread_reader.read_vars() + self.vars = data_per_thread_reader.get_vars() + + def test_user_page(): + http.get("https://blazedemo.com/users/" + self.vars.user_id).assert_ok() +``` + +## Execution results + +Apiritif writes output data from tests in `apiritif.#.csv` files by default. Here `#` is number of executing process. +The output file is similar to this: +```csv +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,2 +``` +It contains test and transaction results for executed tests by one process. + +### Environment Variables + +There are environment variables to control length of response/request body to be written into traces and logs: + * `APIRITIF_TRACE_BODY_EXCLIMIT` - limit of body part to include into exception messages, default is 1024 + * `APIRITIF_TRACE_BODY_HARDLIMIT` - limit of body length to include into JSON trace records, default is unlimited + + + + +%package help +Summary: Development documents and examples for apiritif +Provides: python3-apiritif-doc +%description help +# Apiritif + +Apiritif is a number of utilities aimed to simplify the process of maintaining API tests. +Apiritif tests fully based on python nose tests. This library can help you to develop and run your existing tests. +In order to create any valid tests for Apiritif you can read [nose test documentation](https://nose.readthedocs.io/en/latest/testing.html). + +Check Apiritif version with the following command: +``` +python -m apiritif -- version +``` + +Here described some features of Apiritif which can help you to create tests more easily. + +## Overview + +## HTTP Requests + +Apiritif allows to use simple `requests`-like API for making HTTP requests. + +```python +from apiritif import http + +response = http.get("http://example.com") +response.assert_ok() # will raise AssertionError if request wasn't successful +``` + +`http` object provides the following methods: +```python +from apiritif import http + +http.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: +```python +def get(address, # URL for the request + params=None, # URL params dict + headers=None, # HTTP headers + cookies=None, # request cookies + data=None, # raw request data + json=None, # attach JSON object as request body + encrypted_cert=None, # certificate to use with request + allow_redirects=True, # automatically follow HTTP redirects + timeout=30) # request timeout, by default it's 30 seconds +``` + +##### Certificate usage +Currently `http` supports `pem` and `pkcs12` certificates. +Here is an example of certificate usage: +```python +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 Targets + +Target 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. + + +```python +from apiritif import http + +qa_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: +```python +target = apiritif.http.target( + address, # target base address + base_path=None, # base path prepended to all paths (e.g. '/api/v2') + use_cookies=True, # use cookies + additional_headers=None, # additional headers for all requests + keep_alive=True, # reuse opened HTTP connection + auto_assert_ok=True, # automatically invoke 'assert_ok' after each request +) +``` + + +## Assertions + +Apiritif responses provide a lot of useful assertions that can be used on responses. + +Here's the list of assertions that can be used: +```python +response = http.get("http://example.com/") + +# assert that request succeeded (status code is 2xx or 3xx) +response.assert_ok() +# assert that request has failed +response.assert_failed() + +# status code based assertions +response.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 string +response.assert_in_body(member) + +# assert that response body doesn't contain a string +response.assert_not_in_body(member) + +# search (or match) response body with a regex +response.assert_regex_in_body(regex, match=False) +response.assert_regex_not_in_body(regex, match=False) + +# assert that response has header +response.assert_has_header(header) + +# assert that response has header with given value +response.assert_header_value(header, value) + +# assert that response's headers contains a string +response.assert_in_headers(member) +response.assert_not_in_headers(member) + +# search (or match) response body with a regex +response.assert_regex_in_headers(member) +response.assert_regex_not_in_headers(member) + +# assert that response body matches JSONPath query +response.assert_jsonpath(jsonpath_query, expected_value=None) +response.assert_not_jsonpath(jsonpath_query) + +# assert that response body matches XPath query +response.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 item +response.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: +```python + +response = http.get("http://example.com/") +response.assert_ok().assert_in_body("Example") +``` + +## Transactions + +Apiritif allows to group multiple requests or actions into a transaction using a `transaction` context 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 for `users/all` page even if something wrong with previous actions. + +```python +def test_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. + +```python +def test_with_login(): + with apiritif.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 to `users/all` page 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 transactions + +`smart_transaction` is advanced option for test flow control (stop or continue after failed test method). +Let see another test method example: + +```python +class Tests(TestCase): + def test_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 under `users` space if it is not available. +For this purpose we can use `smart_transaction`. + +```python +class Tests(TestCase): + def setUp(self): + apiritif.put_into_thread_store(func_mode=True) + + def test_available_pages(): + http.get("https://blazedemo.com/").assert_ok() + + with apiritif.smart_transaction('Availability check'): + http.get("https://blazedemo.com/users").assert_ok() + + with apiritif.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 into `smart_transaction` which 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 about `apiritif.put_into_thread_store(func_mode=True)`, this is test execution mode for apiritif. +We can execute all of the transactions in test no matter what or stop after first failed transaction. +This flag tells to apiritif "Stop execution if some transaction failed". `False` says "Run till the end in any case". + +##### Nose Flow Control +It's one more feature based on smart transactions. It changes `func_mode` if necessary to execute whole teardown block, +intended to finalize all necessary things. + +```python +def test_flow-control(self): + try: + self._method_with_exception() + self._skipped_method() + finally: + apiritif.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 of `tearDown` method of nose: +1. all parts of teardown stage will be executed as mentioned above (will be interrupted in regular nose execution) +2. results of teardown steps will be written by apiritif SampleWriter into output file (nose lost them as tearDown isn't recognised as test). + +##### Graceful shutdown +Somethimes 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 apiritif with GRACEFUL environment variable pointed to any file name. +Apiritif will be interrupted as soon as the file is created. + +## CSV Reader +In order to use data from csv file as test parameters Apiritif provides two different csv readers. +Simple `CSVReader` helps you to read data from file line by line and use this data wherever you need: + +```python +data_reader = apiritif.CSVReader('---path to required file---') +class Tests(TestCase): + def test_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. +`CSVReaderPerThread` helps to solve this problem: + +```python +data_per_thread_reader = apiritif.CSVReaderPerThread('---path to required file---') +class Tests(TestCase): + def setUp(self): + data_per_thread_reader.read_vars() + self.vars = data_per_thread_reader.get_vars() + + def test_user_page(): + http.get("https://blazedemo.com/users/" + self.vars.user_id).assert_ok() +``` + +## Execution results + +Apiritif writes output data from tests in `apiritif.#.csv` files by default. Here `#` is number of executing process. +The output file is similar to this: +```csv +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,2 +``` +It contains test and transaction results for executed tests by one process. + +### Environment Variables + +There are environment variables to control length of response/request body to be written into traces and logs: + * `APIRITIF_TRACE_BODY_EXCLIMIT` - limit of body part to include into exception messages, default is 1024 + * `APIRITIF_TRACE_BODY_HARDLIMIT` - limit of body length to include into JSON trace records, default is unlimited + + + + +%prep +%autosetup -n apiritif-1.1.3 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-apiritif -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 1.1.3-1 +- Package Spec generated |