In actual scenarios, each user’s device_sn is different, so we should parameterize the request parameters, which is also called parameterization . In the meanwhile, the sign field is calculated with other header fields, thus it may change significantly if any header field changes slightly.
However, the test cases are only YAML documents, it is impossible to generate parameters dynamically in such text. Fortunately, we can combine Python scripts with YAML test cases in ApiTestEngine .
To achieve this goal, we can utilize import_module_functions and variable_binds mechanisms.
To be specific, we can create a Python file ( examples/utils.py ) and implement the related algorithm in it. Since we want to import this file, so we should put a __init__.py in this folder to make it as a Python module.
import hashlib import hmac import random import string SECRET_KEY = "DebugTalk" def get_sign(*args): content = ''.join(args).encode('ascii') sign_key = SECRET_KEY.encode('ascii') sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest() return sign def gen_random_string(str_len): random_char_list = [] for _ in range(str_len): random_char = random.choice(string.ascii_letters + string.digits) random_char_list.append(random_char) random_string = ''.join(random_char_list) return random_stringAnd then, we can revise our demo test case and reference the functions. Suppose the revised file named quickstart-demo-rev-2.yml
- test: name: get token import_module_functions: - examples.utils variable_binds: - user_agent: 'iOS/10.3' - device_sn: ${gen_random_string(15)} - os_platform: 'ios' - app_version: '2.8.6' request: url: :5000/api/get-token method: POST headers: user_agent: $user_agent device_sn: $device_sn os_platform: $os_platform app_version: $app_version json: sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)} extract_binds: - token: content.token validators: - {"check": "status_code", "comparator": "eq", "expected": 200} - {"check": "content.token", "comparator": "len_eq", "expected": 16} - test: name: create user which does not exist request: url: :5000/api/users/1000 method: POST headers: device_sn: $device_sn token: $token json: name: "user1" password: "123456" validators: - {"check": "status_code", "comparator": "eq", "expected": 201} - {"check": "content.success", "comparator": "eq", "expected": true}In this revised test case, we firstly import module functions in import_module_functions block by specifying the Python module path, which is relative to the current working directory.
To make fields like device_sn can be used more than once, we also bind values to variables in variable_binds block. When we bind variables, we can not only bind exact value to a variable name, but also can call a function and bind the evaluated value to it.
When we want to reference a variable in the test case, we can do this with a escape character $ . For example, $user_agent will not be taken as a normal string, and ApiTestEngine will consider it as a variable named user_agent , search and return its binding value.
When we want to reference a function, we shall use another escape character ${} . Any content in ${} will be considered as function calling, so we should guarantee that we call functions in the right way. At the same time, variables can also be referenced as parameters of function.
Optimize test case: overall config blockThere is still one issue unsolved.
The device_sn field is defined in the first API test case, thus it may be impossible to reference it in other test cases. Context separation is a well-designed mechanism, and we should obey this good practice.
To handle this case, overall config block is supported in ApiTestEngine . If we define variables or import functions in config block, these variables and functions will become global and can be referenced in the whole test set.
# examples/quickstart-demo-rev-3.yml - config: name: "smoketest for CRUD users." import_module_functions: - examples.utils variable_binds: - device_sn: ${gen_random_string(15)} request: base_url: :5000 headers: device_sn: $device_sn - test: name: get token variable_binds: - user_agent: 'iOS/10.3' - os_platform: 'ios' - app_version: '2.8.6' request: url: /api/get-token method: POST headers: user_agent: $user_agent os_platform: $os_platform app_version: $app_version json: sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)} extract_binds: - token: content.token validators: - {"check": "status_code", "comparator": "eq", "expected": 200} - {"check": "content.token", "comparator": "len_eq", "expected": 16} - test: name: create user which does not exist request: url: /api/users/1000 method: POST headers: token: $token json: name: "user1" password: "123456" validators: - {"check": "status_code", "comparator": "eq", "expected": 201} - {"check": "content.success", "comparator": "eq", "expected": true}