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

review

In the previous section we looked at decorators for asynchronous methods, and in these sections we continue to focus on the logic of the use case. Having completed the data constructor-related functionality, let’s make a series of modifications to support data-driven.

Today I’m going to talk about the basic Model.

This section provides an overview

Warning: there will be a lot of changes in the next few sections, so you can pull the code directly to see the changes.

Github address: github.com/wuranxu/pit…

The general change is that the parameters of the use case are no longer single, and multiple sets of data are tested for the same scenario.

conceived

Our case is currently written dead data, take our query exists user interface as an example, we write a case, and then write the name in the URL:

In fact, the name can have many groups of data, if only the name changes, have to write many almost the same case, it is like sheep fart goat fart.

So we can pull the data out, and as anyone who’s used DDT knows, UNITtest plus DDT can be data-driven, and it will generate N use cases for you.

So our goal is to have the same effect.

Talk so much nonsense, I just don’t say how to do!! In fact, we maintain a data table, which specifically stores test data, and then with case through case_id association, when the use case execution data is not good?

Yes, this is the general effect, but there are many obstacles to implementation, we will discuss them later.

Design data sheet

If I had gone by my usual pee routine, I would have designed a watch like this:

Note that this is all pseudo-code
class TestCaseData(BaseModel) :
    id = Column(INT)
    created_at = Column(DATETIME)
    updated_at = Column(DATETIME)
    deleted_at = Column(DATETIME)
    create_user = Column(INT)
    update_user = Column(INT)
Copy the code

These are all basic fields that have to be added in addition to the core fields of the table, so I’m getting a little tired of writing so many fields every time.

Why not define a basic Model and keep only the core fields in the business Model? Once I had this idea, I couldn’t stop, so I spent some time working on it. Here’s how to do it.

Basic design Model

We know that SqlAlchemy owns the BaseModel class, from which all class models must inherit.

So our base Model has to inherit it. Create the file basic.py in the models directory

from datetime import datetime

from sqlalchemy import INT, DATETIME, Column, BIGINT

from app.models import Base


class PityBase(Base) :
    id = Column(INT, primary_key=True)
    created_at = Column(DATETIME, nullable=False)
    updated_at = Column(DATETIME, nullable=False)
    deleted_at = Column(BIGINT, nullable=False, default=0)
    create_user = Column(INT, nullable=False)
    update_user = Column(INT, nullable=False)

    def __init__(self, user, id=0) :
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        self.create_user = user
        self.update_user = user
        self.id = id
Copy the code

Note that we changed deleted_at to a BIGINT instead of a datetime object because of soft deletions. If in doubt, please refer to my previous soft deletions article.

So we’ve defined the PityBase class. Let’s talk about table design. In fact, in our table, you are not sure how many fields the user needs. I think this kind of data is very suitable for mongo, it supports different types of data into a table, also can not define fields.

But since we are using Mysql, we can only write our data fields as JSON data (string), through serialization/deserialization of our requirements. (Although serialization/de-sequencing affects performance)

Test data sheet

""" Test data sheet, used to store test data in various environments for data-driven purposes
__author__ = "miluo"

from sqlalchemy import Column, INT, String, UniqueConstraint, TEXT

from app.models.basic import PityBase


class PityTestcaseData(PityBase) :
    env = Column(INT, nullable=False)
    case_id = Column(INT, nullable=False)
    name = Column(String(32), nullable=False)
    json_data = Column(TEXT, nullable=False)

    __table_args__ = (
        UniqueConstraint('env'.'case_id'.'name'.'deleted_at'),
    )

    __tablename__ = "pity_testcase_data"

    def __init__(self, env, case_id, name, json_data, user, id=0) :
        super().__init__(user, id)
        self.env = env
        self.case_id = case_id
        self.name = name
        self.json_data = json_data
Copy the code

We store env, case_id, name and jSON_data, and set joint unique index to prevent data duplication.

Some of you might wonder what the name is, but the name is actually an identifier for the data, just an individual name, that identifies your set of data.

The __init__ method, which calls __init__() of the parent class, does all the assignment work.

Is that the end of it?

It’s not that simple. We need to make one more change to the PityModel. We need to add an __abstract__ variable and set it to True. If you are careful, try removing the __abstract__ variable and your PityModel will be treated as a new table, which will cause an error.

So that’s it for today, and in the next video we’ll talk about TestcaseData operations and other changes. In the future, if there is any new data table, it will be integrated from PityModel class, which will save a lot of trouble.