Enhancing Httprunner Test Cases with Custom Functions (debugtalk.xx) – Part Four

httprunner version: 4.3.5

HttpRunner v4 introduces a new plugin mechanism, currently supporting custom function implementations in both Python and Go languages. For Go code, it must be compiled into a debugtalk.bin file. The plugin file debugtalk.xx must be placed in the project root directory.

Next, we will discuss how to implement any complex business logic testing using custom functions in Python.

The plugin-python allows for custom functions to be written in Python, requiring only the creation of the debugtalk.py file in the project root directory, with no additional compilation needed. Additionally, using hrp run/boom is compatible with the syntax from versions prior to v4.0. Note: When testing with hrp, it generates a .debugtalk_gen.py file that can be directly called based on the debugtalk.py file; please do not edit this file.

Testing Scenario: In the login API test, the password is transmitted in an encrypted format, so we need to encrypt the password when passing parameters to the API. Here, we encapsulate our encryption method which calls the third-party module: pycryptodome, and the method requires two parameters: the password and the key.

1. Encryption Algorithm, Path: tools.aes_encrypt_decrypt.py

# Standard library import
import base64
# Third-party module import
from Crypto.Cipher import AES
class Encrypt:
    """
    Use AES-CBC symmetric encryption algorithm to encrypt the password, padding mode is PKCS7Padding
    """
    def __init__(self, key, iv):
        self.key = key.encode('utf-8')
        self.iv = iv.encode('utf-8')
    # @staticmethod
    def pkcs7padding(self, text):
        """Plaintext uses PKCS7 padding """
        bs = 16
        length = len(text)
        bytes_length = len(text.encode('utf-8'))
        padding_size = length if (bytes_length == length) else bytes_length
        padding = bs - padding_size % bs
        padding_text = chr(padding) * padding
        self.coding = chr(padding)
        return text + padding_text
    def aes_encrypt(self, content):
        """ AES encryption """
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        # Process plaintext
        content_padding = self.pkcs7padding(content)
        # Encrypt
        encrypt_bytes = cipher.encrypt(content_padding.encode('utf-8'))
        # Re-encode
        result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
        return result
    def aes_decrypt(self, content):
        """AES decryption """
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        content = base64.b64decode(content)
        text = cipher.decrypt(content).decode('utf-8')
        return text.rstrip(self.coding)

2. Define a custom function in debugtalk.py to introduce the encryption algorithm and return the encrypted password.

from tools.aes_encrypt_decrypt import Encrypt
def get_password(target_str: str, ace_key):
    """
    Use AES-CBC symmetric encryption algorithm to encrypt the password
    """
    ace = Encrypt(key=ace_key, iv=ace_key)
    return ace.aes_encrypt(target_str)

3. Call the custom function in the YAML test case to return the encrypted password

config:
  name: "Login API"
  verify: False
  base_url: "https://gitlink.org.cn"
  headers:
    content-type: "application/json"
  variables:
    o_pwd: ****1234
    o_key: **c9**********c1
teststeps:
  -
    name: Send login request
    request:  # request API related parameters
      method: POST
      url: /api/accounts/login.json
      body:
        autologin: 0
        login: floraachy
        password: ${get_password($o_pwd,$o_key)}
    validate:  # assertions
      - eq: ["status_code", 200]
      - eq: ["body.login", '${user}']

Common Pitfalls

  1. When third-party dependencies are introduced in debugtalk, calling custom functions fails, leading to hrp execution failure. During hrp execution, it uses the C:\Users\username.hrp\venv virtual environment, and the failure occurs because the third-party dependency was not installed in this virtual environment.

There are two solutions (source: https://github.com/httprunner/httprunner/discussions/1341):

Use the python3 in the Scripts directory to download the third-party dependency, path reference: C:\Users\username.hrp\venv\Scripts\python3 install pycryptodome

Install directly on the testing machine: pip install pycryptodome, then change the include-system-site-packages in the pyvenv.cfg file of the virtual environment from false to true.

Path reference: C:\Users\username.hrp\venv\pyvenv.cfg Modify content: include-system-site-packages = true

2. If the password contains special characters, calling the custom function fails, leading to hrp execution failure.

Solution: Treat the password as a global variable, and then directly use the global variable as a parameter in the custom function.

# ---- Complete YAML test case reference above-------
# Treat the password as a global variable
  variables:
    o_pwd: +=-===@123
    o_key: 23333333333333
    
# In the custom function, directly use the global variable as a parameter
        password: ${get_password($o_pwd,$o_key)}

Note: Here, when introducing global variables in the custom function, only variable names can be used, and cannot use {variable name}, otherwise execution will also report an error.

Leave a Comment