The test platform series (83) preconditions support Redis statements

Hello ~ I'm Milo!

I'm building an open source interface testing platform from 0 to 1, and I'm also writing a complete set of corresponding tutorials. I hope you can support me.

Welcome to my official account test development pit, get the latest article tutorial!

review

In the last section, we played a wild game and solved the problem of repeated execution of APScheduler. In the previous section, we have written the online execution function of Redis.

In this section, we have to apply it to the preconditions.

Achievement display

Because bloggers code first and then write articles. Therefore, we can show the results. Today's changes are as follows:

  • Preconditions (REDIS type) are supported

thinking

Preconditions to support Redis, the idea is actually very simple, and the mode is similar to SQL. So we can copy the SQL code.

Before, our executors were placed under the utils package, which is actually very unfriendly. In order to show professionalism, I decided to pull the Executor from utils and put it under the new directory core.

The core directory is intended to put some core methods of use case execution. Let's wait and see.

How to modify a file without affecting other data

This requires the great pycham. In fact, after we create a new core directory, we can directly move the executor.py to the core directory.

Moving files through pycharm is not only as simple as moving files, but also helps you deal with the packages introduced into this file. If the path changes, it will automatically help update. If other files reference executor.py, it will also help you update.

With such a powerful ide as support, I can play at will.

Split Executor

With more and more case execution logic, our executor.py can't hold. It's not that a py file can't hold 1000 lines of code, but we don't need to make it so bloated.

What we can remove most is the function that executes the construction method. First, we have several fixed construction methods. Let's see how we write when we only support sql and use cases:

    async def execute_constructor(self, env, index, path, params, req_params, constructor: Constructor):
        if not constructor.enable:
            self.append(f"current path: {path}, Construction method: {constructor.name} Closed, Do not proceed")
            return
        if constructor.type == 0:
            try:
                data = json.loads(constructor.constructor_json)
                case_id = data.get("case_id")
                testcase, _ = await TestCaseDao.async_query_test_case(case_id)
                self.append(f"current path: {path}, The first{index + 1}Strip construction method")
                # The description is case
                executor = Executor(self.logger)
                new_param = data.get("params")
                if new_param:
                    temp = json.loads(new_param)
                    req_params.update(temp)
                result, err = await executor.run(env, case_id, params, req_params, f"{path}->{testcase.name}")
                if err:
                    raise Exception(err)
                if not result["status"]:
                    raise Exception(f"assertion failure , Assertion data: {result.get('asserts', 'unknown')}")
                params[constructor.value] = result
                # await self.parse_params(testcase, params)
            except Exception as e:
                raise Exception(f"{path}->{constructor.name} The first{index + 1}Constructor execution failed: {e}")
        elif constructor.type == 1:
            # The description is an sql statement
            try:
                self.append(f"current path: {path}, The first{index + 1}Strip construction method")
                data = json.loads(constructor.constructor_json)
                database = data.get("database")
                sql = data.get("sql")
                self.append(f"The current construction method type is sql, Database name: {database}\nsql: {sql}\n")
                sql_data = await DbConfigDao.execute_sql(env, database, sql)
                params[constructor.value] = sql_data
                self.append(f"The current constructor returns a variable: {constructor.value}\n Return value:\n {sql_data}\n")
            except Exception as e:
                raise Exception(f"{path}->{constructor.name} The first{index + 1}Constructor execution failed: {e}")

It can be seen that only two methods are very bloated. In fact, for a more readable writing method, we need to disassemble several construction methods. After disassembly, there is a run method respectively. In this way, we only need to judge the type and execute the corresponding run method.

New abstract base class

from abc import ABC

from app.models.constructor import Constructor


class ConstructorAbstract(ABC):

    @staticmethod
    def run(executor, env, index, path, params, req_params, constructor: Constructor, **kwargs):
        pass

The base class method is similar to the abstract class of Java. It defines a class and cannot be instantiated without implementing the methods inside.

  • sql implementation

  • testcase implementation

There are some complex changes in test cases:

Generally speaking, we call the run method of preconditions by case, but what happens because the test case needs to call the executor again?

The executor refers to testcase and the executor refers to a loop, which is prohibited in python. After all, you're cheating!

So the method adopted here is to pass my Executor class as a parameter == (enough soil)

  • redis implementation

    In fact, we have written similar methods when implementing redis, but before redis was executed according to id, now we need to change it and execute it according to name.

    The implementation according to name is a bit of a last resort, mainly because our case is written to adapt to multiple environments. If the id is passed in, redis corresponds to the configuration of a fixed environment and cannot adapt to multiple environments.

    Like sql, we use env+name to determine the redis connection (the name remains unchanged, and the redis connection becomes dynamic when env switches)

redis needs to change the following command:

The parameters passed in here are not just ID. when you pass in id=xx, you query according to ID, and when you pass in name=xxx, you query according to name, which is compatible with the online redis interface.

executor CLOSEOUT

Final effect

It seems clear at a glance. In fact, it is a bit like interface and impl.

That's all for today ~ we haven't figured out what to do in the next wave. We may improve the post conditions or execute case online or qiniu cloud oss. There's a long way to go. It's already 83 knots.

Online experience: http://test.pity.fun

If you like, you can give Pitty a star. Oh, your star is my motivation ==

Tags: Python React Testing FastAPI

Posted on Tue, 30 Nov 2021 15:46:26 -0500 by imartin