Automatic project configuration or use case file format Recommendation -- yaml

The file format used in the interface test of pytest - yaml, and the corresponding Python Library - PyYaml, are familiar. It can be used as the configuration file or use case file of your automated test framework.

yaml is a lighter file format than xml and json. It is also simpler and more powerful. It can represent the structure through indentation. It sounds very paired with Python, doesn't it?

The introduction of yaml is not repeated here. If you are interested, you can go to Baidu by yourself. Let's talk about its basic grammar first, or cooperate with PyYaml:

1, PyYaml

PyYaml is a Python module for yaml file operation, which is very simple to use.

1. Installation:

(venv) jinjideleishen:Test_framework leiyuxing$ pip install PyYaml

Collecting PyYaml

  Using cached PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl (253 kB)

Installing collected packages: PyYaml

Successfully installed PyYaml-5.4.1

Or here https://pyyaml.org/ Download the corresponding version of the package and install it manually.

2. Use:

It is very simple to use. Like json and pickle, load and dump are enough for us.

2.1 load()

import yaml



yaml_str = """

name: lucas

age: 18

job: Tester

"""



y = yaml.load(yaml_str)

print(y)

result:

{'name': 'lucas', 'age': 18, 'job': 'Tester'}

2.2 dump()

import yaml



python_obj = {"name": u"lucas",

              "age": 18,

              "job": "Tester"

              }



y = yaml.dump(python_obj, default_flow_style=False)

print(y)

result:

age: 18

job: Tester

name: lucas

The above are just two simple applications, and load_all(),dump_all() and so on, we usually use these four enough.

2, yaml syntax

With the above foundation, let's take a look at the syntax of yaml. First, let's prepare the environment for testing syntax:

1. Create test Yaml file, where we practice grammar

2. Create testyaml Py file, which is used to view the effect after Python execution. The contents are as follows:

# -*- coding: utf-8 -*-

import yaml



y = yaml.load(file('test.yaml', 'r'))

print y

Well, let's look at the grammar:

3. Basic rules

Case sensitive

Use indentation to represent hierarchical relationships

Tab s are not allowed when indenting, only spaces are allowed

The number of indented spaces is not important, as long as the elements of the same level are aligned to the left

#Represents a comment that is ignored from the beginning to the end of the line

4. yaml to dictionary

Mapping or dictionary representation is supported in yaml, as follows:

# The following format will be a dict in Python

name: lucas

age: 18

job: Tester

Output:

{'name': 'lucas', 'age': 18, 'job': 'Tester'}

5. yaml to list

yaml supports the representation of lists or arrays, as follows:

# The following format will be a list in Python

- lucas

- 18

- Tester

Output:

[u'luacs', 18, 'Tester']

6. Composite structure

Dictionaries and lists can be combined as follows:

# The following format reads that Python is a list containing dict

- name: lucas

  age: 18

  job: Tester

- name: James

  age: 30

Output:

[{'job': 'Tester', 'age': 0, 'name': u'lucas'}, {'age': 30, 'name': 'James'}]

7. Basic type

There are the following basic types in yaml:

7.1 string

7.2 integer

7.3 floating point

7.4 Boolean

7.5 null

7.6 time

7.7 date

Let's write an example:

# This example outputs a dictionary where value includes all basic types

str: "Hello World!"

int: 110

float: 3.141

boolean: true  # or false

None: null  # You can also use the ~ sign to represent null

time: 2016-09-22t11:43:30.20+08:00  # ISO8601, written in Baidu

date: 2016-09-22  # Also ISO8601

Output:

{'str': 'Hello World!', 'int': 110, 'float': 3.141, 'boolean': True, 'None': None, 'time': datetime.datetime(2016, 9, 22, 11, 43, 30, 200000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800))), 'date': datetime.date(2016, 9, 22)}

If the string does not have spaces or special characters, you do not need quotation marks, but if there are spaces or special characters, you need quotation marks

str: lucas

str1: "Hello World"

str2: "Hello\nWorld"

Output:

{'str2': 'Hello\nWorld', 'str1': 'Hello World', 'str': u'lucas'}

Pay attention to the difference between single quotation marks and double quotation marks. Special characters in single quotation marks will be escaped when transferred to Python, that is, they will be output as is in the end, double quotation marks will not be escaped by Python, and special characters will be output in the end; It may be awkward. Let's take an example to understand:

str1: 'Hello\nWorld'

str2: "Hello\nWorld"



# -*- coding: utf-8 -*-

import yaml



y = yaml.load(file('test.yaml', 'r'))

print y['str1']

print y['str2']

Output:

Hello\nWorld

Hello

World

As you can see, the '\ n' in single quotation marks is finally output, and the '\ n' in double quotation marks is finally escaped into carriage return

In string processing, the meaning of writing multiple lines,'| ',' > ',' + ',' - 'is not discussed here.

8. Quote

&And * for reference

name: &name Grey blue

tester: *name

This is equivalent to the following script:

name: lucas

tester:  lucas

Output:

{'name': u' lucas', 'tester': u' lucas'}

9. Mandatory conversion

yaml can be cast, with!! Implementation, as follows:

str: !!str 3.14

int: !!int "123"

Output:

{'int': 123, 'str': '3.14'}

Obviously, 123 is strongly converted to int type, while 3.14 of float type is strongly converted to str type. In addition, PyYaml also supports conversion to Python/object types, which we will discuss later.

10. Sub paragraph

In the same yaml file, you can use - -- to segment, so that multiple documents can be written in one file

---

name: James

age: 20

---

name: lucas

age: 18

At this time, we have to use our load_ The all () method comes out, load_ The all () method will generate an iterator, which can be output with for:

# -*- coding: utf-8 -*-

import yaml



ys = yaml.load_all(file('test.yaml', 'r'))

for y in ys:

    print y

Output:

{'age': 20, 'name': 'James'}

{'age': 18, 'name': 'lucas'}

The corresponding dump is also available_ The all () method means that multiple segments are output to a file, for example:

# -*- coding: utf-8 -*-

import yaml



obj1 = {"name": "James", "age": 20}

obj2 = ["lucas", 18]



with open('test.yaml', 'w') as f:

    yaml.dump_all([obj1, obj2], f)

Open test Yaml look:

{age: 20, name: James}

--- [Lily, 19]

dump() and dump_ The all () method can pass in a list or a serializable generator, such as range(10), as follows:

# -*- coding: utf-8 -*-

import yaml



y = yaml.dump(range(10))

print y

Output:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

3, Constructors, delegates, resolvers

1. yaml.YAMLObject

yaml.YAMLObject uses metaclasses to register a constructor (i.e. _init_ () method in the code), which allows you to convert yaml node into Python object instance, and uses the descriptor (i.e. _repr_ () function in the code) to enable you to convert Python object into yaml node. See the code:

# -*- coding: utf-8 -*-

import yaml


class Person(yaml.YAMLObject):

    yaml_tag = '!person'



    def __init__(self, name, age):

        self.name = name

        self.age = age



    def __repr__(self):

        return '%s(name=%s, age=%d)' % (self.__class__.__name__, self.name, self.age)



james = Person('James', 20)



print yaml.dump(james)  # Convert Python object instance to yaml



lily = yaml.load('!person {name: Lily, age: 19}')



print lily  # yaml to Python object instance

Output:

!person {age: 20, name: James}


Person(name=Lily, age=19)

2. yaml.add_constructor and yaml add_ representer

You may not want to define normal classes through the above metaclasses, so you can use these two methods

# -*- coding: utf-8 -*-

import yaml





class Person(object):

    def __init__(self, name, age):

        self.name = name

        self.age = age



    def __repr__(self):

        return 'Person(%s, %s)' % (self.name, self.age)



james = Person('James', 20)

print yaml.dump(james)  # Before adding the indicator





def person_repr(dumper, data):

    return dumper.represent_mapping(u'!person', {"name": data.name, "age": data.age})  # mapping indicator for dict



yaml.add_representer(Person, person_repr)  # Use add_ The presenter method adds a presenter to the object

print yaml.dump(james)  # After adding the indicator





def person_cons(loader, node):

    value = loader.construct_mapping(node)  # mapping constructor for dict

    name = value['name']

    age = value['age']

    return Person(name, age)



yaml.add_constructor(u'!person', person_cons)  # Use add_ The constructor method adds a constructor for the specified yaml tag

lily = yaml.load('!person {name: Lily, age: 19}')

print lily

Output:

!!python/object:__main__.Person {age: 20, name: James}


!person {age: 20, name: James}


Person(Lily, 19)

The first line is before the indicator is added. How ugly! The middle line has become a standard format after adding an indicator. A constructor is added below to make it possible to! The Person tag is converted to a Person object.

Construct is used here_ Mapping, and many other constructs_ document, construct_object,construct_scalar,construct_sequence,construct_pairs, how to use it, you can study it yourself, look at the API and the source code.

The same is true for the corresponding representative. There are many. Only representative is used here_ Mapping, other examples are not explained.

3. add_implicit_resolver

If you don't want to write labels every time, you can also use add_ implicit_ The resolver method adds a parser, and then it can parse the unlabeled basic elements of the specified style into the corresponding Python object.

4. Conclusion

Yaml is a very clear and concise format, which is very compatible with Python and easy to operate. When building an automated test framework, we can use yaml as a configuration file or use case file. The following is an example of a use case from Python restful interface framework pyresttest:

# Test using included Django test app

# First install python-django

# Then launch the app in another terminal by doing

#   cd testapp

#   python manage.py testserver test_data.json

# Once launched, tests can be executed via:

#   python resttest.py http://localhost:8000 miniapp-test.yaml

---

- config:

    - testset: "Tests using test app"



- test: # create entity

    - name: "Basic get"

    - url: "/api/person/"

- test: # create entity

    - name: "Get single person"

    - url: "/api/person/1/"

- test: # create entity

    - name: "Get single person"

    - url: "/api/person/1/"

    - method: 'DELETE'

- test: # create entity by PUT

    - name: "Create/update person"

    - url: "/api/person/1/"

    - method: "PUT"

    - body: '{"first_name": "Gaius","id": 1,"last_name": "Baltar","login": "gbaltar"}'

    - headers: {'Content-Type': 'application/json'}

- test: # create entity by POST

    - name: "Create person"

    - url: "/api/person/"

    - method: "POST"

    - body: '{"first_name": "Willim","last_name": "Adama","login": "theadmiral"}'

    - headers: {Content-Type: application/json}

This implementation and application can be seen in my article: Development practice of interface automation test based on Pytest framework (package church!!!)

Keywords: Python AI

Added by mariocesar on Wed, 15 Dec 2021 04:01:15 +0200