This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

Hello ~ I’m Milo! I am building an open source interface testing platform from 0 to 1, and I am also writing a complete tutorial corresponding to it. I hope you can support it. Welcome to pay attention to my public number test development pit goods, get the latest article tutorial!

review

In the last session, we played a game to solve the repetitive execution problem of APScheduler. In the previous section, we wrote the online execution capabilities of Redis.

So in this video we’re going to have to apply that to preconditions.

Results show

Because bloggers are coding first, then write. So it can show results. Today’s changes are as follows:

  • Support for preconditions (REDIS type)

Train of thought

To support Redis, the idea is actually very simple and the schema is similar to SQL. So we can just copy the SQL code.

Before, our executors were all under utils, which was pretty unfriendly. To be professional, I’ve decided to take Executor out of utils and put it under the new directory core.

This core directory is intended to contain some core methods for use case execution, so let’s see.

How can I modify a file without affecting other data

This requires great Pycharm. We can create a new core directory and move executor.py directly to the core directory.

By moving files through PyCharm, it doesn’t just move files. It also helps you figure out which packages are introduced in the file, automatically updates them if the path changes, and also updates them if other files reference executor.py.

With such a powerful IDE to back it up, I can play it any way I want.

Break up the Executor

As the logic of case execution increases, our executor.py can’t handle it. It’s not that a PY file can’t hold 1000 lines of code, it’s just that we don’t need to make it that bloated.

The only thing we can remove is a function that executes a constructor. We have a fixed number of constructors. Let’s look at what we did when we only supported 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, no further execution")
            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}In the first{index + 1}Strip construction method")
                # 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 "failed to assert, assert 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}{index + 1}Three constructor methods failed to execute:{e}")
        elif constructor.type= =1:
            SQL statement
            try:
                self.append(F "Current path:{path}In the first{index + 1}Strip construction method")
                data = json.loads(constructor.constructor_json)
                database = data.get("database")
                sql = data.get("sql")
                self.append(SQL > select * from 'SQL';{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 the variable:{constructor.value}\n Return value :\n{sql_data}\n")
            except Exception as e:
                raise Exception(f"{path}->{constructor.name}{index + 1}Three constructor methods failed to execute:{e}")
Copy the code

As you can see, it’s already very bloated with only 2 methods. A more readable way to write this would be to separate the constructors and have a run method so that we can just determine what type is and execute the corresponding run method.

Create an 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

Copy the code

Base class methods are similar to Java’s Abstract class, which defines a class that cannot be instantiated without implementing its methods.

  • SQL implementation

  • The testcase implement

Here are some complex changes to the test case:

Normally, we have run methods that case calls preconditions, but since the test case needs to call Executor again, what happens?

Executor reference TestCase Reference executor, that is, reference loop, which is forbidden in Python because you are nesting!

So the crude approach here is to pass in my Executor class as an argument =. = (dirt enough)

  • Redis implementation

    We have already written a similar method when executing redis, but since we used to execute redis based on id, we now need to execute redis based on name.

    Execution according to name is a little unavoidable, mainly because our case writing is to adapt to multiple sets of environment, if the id is passed, then redis corresponds to the configuration of fixed environment, can not adapt to multiple environments.

    As with SQL, we use env+name to determine redis connections (name does not change, and redis connections become dynamic when env switches)

Redis execution command needs to be changed.

Id =xx; id=xx; id= XXX; name= XXX;

Executor ending

The final result

It looks obvious, but it’s kind of like Interface and IMPL.


Today’s content is here ~ the next wave of what to do has not been decided, may improve the post-condition or online implementation of case or seven ox cloud OSS bar. There’s a long way to go. It’s 83 knots.

Online experience: test.pity. Fun

Oh, your star is my motivation