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!!!)