My first little pytest plugin

I test our backend via an API interface. It is a json rpc api that talks over a websocket. The test framework that I created is written in python. The testrunner is py.test. py.test already has a lot of plugins that are very handy.

All went well, until last week. The latest tests I wrote should not run as is. Some of them where tests that did a calibration. Others where tests that should run before the calibration was done.  I did find a pytest plugin that could order the tests and run them in that order. But I was not satisfied with the plugin itself. The markers where not chosen well for our project.

So I decided to have our own simple plugin that fits our needs. I wanted something like this:

This is very easy to implement. I created a file sorttests.py in a plugin folder with the following content:

This function is called by pytest when the collection of all tests is completed. By altering the items list, we can manipulate the tests that are running, also the order of the tests.

Now I can use the marker run_when_not_calibrated, just what I wanted.

I only needed to put the following line in the conftest.py file to enable the plugin. This is needed because I did not create a setup for the plugin yet.

So with a little bit of extra code, I easily can manipulate the order of the tests.

How I setup our test documentation

What are you going to test for this story?

That was a question that I ask myself a lot. This is a question that needs to be answered for every user story that I test.

We do have a lot of automated tests. Tests that are written in python. It are tests on API level, so without interaction of the user interface.  I also have to test manually. Not every part of our huge system can be automated. For manual testing, I created some test documentation. That documentation contains what will be tested. I do not just write in detail what I look for, but in general. There should be room for doing crazy stuff while testing too.

How should the system react? What should the software do? These are some of the questions that are answered in the testing documentation.

Documentation?

The first version of this test documentation was a document written in word. It worked, because I was the only one who edited the document. When the company grew, this was also not suitable anymore. I and my colleagues can not work on that document at the same time. That is the reason that I looked at a new format for my test documentation.

What did I want?

  • It must be simple to edit.
  • No code language like html or so. Preferable in markdown or another simple format.
  • Different users should be able to edit the same document at the same time.
  • It should be placed in version control, so that we can merge it, like other code.

The new documentation

Sphinx is a tool that makes it easy to create intelligent and beautiful documentation, written by Georg Brandl and licensed under the BSD license.

Our testing code was already in python. Python has some nice documentation. Why not use the same documentation framework as the python community uses? That could be a good alternative. I found the Sphinx project. This project seems to be what I wanted. On the main page the first sentence was promising. It talks about easy to create and beautiful documentation.

The first thing I did was installing Sphinx. So I entered:

That was an easy one. The next one was in an empty directory, I typed the command

That is also explained in the First Steps With Sphinx Tutorial.
This sphinx-quickstart program asked me a lot of questions. I answered them one by one. One of the last question was if it should generate a make file. I answered yes. Now I do not need to remember some large command like:

Now the program has generated some files, that can be placed in version control. Building the documentation is very simple:

Test documentation itself needs to be in reStructuredText format. It is also very easy to learn. It is nearly the same as markdown. More information on reStructuredText can be found on their webpage.

Because now the complete test documentation is in text files, it is possible to store it in version control. We use git in our company, so that infrastructure can be re-used. I also make use of our jenkins build server. This build server generates the documentation for us after each check-in. And that documentation is afterwards copied to a web server. Now everybody can have a look at what is tested at any time.

It is a system that is easy to use now, and it is transparent for everybody. Isn’t that what I wanted?

Mutable Default Arguments in python

Our team has some simulators and integration tests written in python. For the most part, Python is a simple language that avoid surprises. That is why we took this language as the language used for testing. There are some few situations that can be confusing though. One of the most common surprise are the default arguments. More specific, mutable default arguments.

Suppose you have a function like the following:

Of course, you can call this function with or without any parameters. What happens in the following situation?

You expect that a new list is created each time the function is called, because the second parameter is not provided. So you expect the following output:

But that is not the case. What you see on your console is the following:

A new list is created only once. It is only created on definition time of the function and not on calling time like in other languages. This means that if you mutated the object, you also have mutated it for future usages as well.
We should create an object each time the function is called. This can be done by adding another default argument that signals us, that there is no argument provided.

And now the output is the one we did expect.

Automatic testing framework

We have a build chain. After each code change in our version control (git) there is a build on our continuous integration server. This build creates each time an installable executable and runs some integration tests.

The integration tests will interact with our backend. We have a frontend and a backend. That backend talks to another service, plc’s and other hardware. For the moment there is not yet communication with PLC’s implemented, but it will be soon. For the other hardware, I wrote a hardware simulator in python. I did prefer erlang, but my colleagues objected about it, because the erlang is too difficult. I find that a stupid reason, but not worth arguing about. So I wrote the simulator in python. It was not that hard, because we have some commands that just interrogates some memory in hardware. If a register in memory is set to a value, the real hardware interacts with it. My simulator does not interact with all registers, only with the registers that have state in it.

After I wrote the simulator, I wrote some tests. Integration tests that calls the backend. Why not the frontend? Because it is easier to script calls to the backend, and they are less error prone. At first I choose for the robot framework. This is a keyword driven framework written in python. All tests can be put in version control, so this means that we also can do code reviews and other stuff that the developers do.

I did write the tests in robot framework, but the implementation of the keywords did have an implementation in a library. And now I am happy that I did it this way. Because at a retrospective in the past, we decided that our developers should also write integration tests. The objection was, 2 languages for testing? Can it not be only in python? So I searched a little bit and found py.test. This unit test framework can also run other tests that run longer. It is a nice alternative and the impact to convert from robot to py.test was not that hard. Only the tests itself must be rewritten, because I did implement the driver code already in python. A few sprints later, we had a decent testing framework. And I am also happy, because every team member can now write integration tests, and they are, so I am a happy tester now.

Dynamische properties

Python is een leuke taal. Je kan een klasse hebben die dat properties heeft.

Nu zien we dat de width en de height property eigenlijk de zelfde functie is, uitgezonderd van 1 waarde, namelijk de key in de self.fields dictionary. Zou het niet mogelijk zijn om de properties dynamisch aan te maken? Aangezien het python is, is het antwoord positief.

De oplossing ligt ook verborgen in een vorige post. Het gebruik van descriptors die we dynamisch gaan aanmaken kunnen hier voor dienen.

Als er nu een klasse gemaakt wordt die een get heeft en die dat de hardware oproept, en deze dan dynamisch in de Rechthoek klasse wordt aangemaakt, is het probleem opgelost.

De laatste regels code kunnen nog anders geschreven worden, zodat we ook hier geen code wijzigingen meer moeten doen, indien onze fields zouden wijzigen.

Nu is een nieuwe property eenvoudig aangemaakt door enkel de self.fields aan te passen. Eenvoudig en simpel.

Property decorator

Python heeft een leuk concept, namelijk dat van een property. Dit concept is enkel beschikbaar bij klassen die overerven van object.

Stel dat we een klasse hebben die alle interactie met een auto maken. We noemen de Klasse Car. Als je met de auto rijdt, druk je op het gaspedaal en een snelheid (speed) te verkrijgen. Dus dit zetten we in de klasse.

Het resultaat van car.speed is dus 6. Wat als we nu een maximumwaarde willen invoeren, want op onze wegen mag je toch niet sneller dan 120 rijden. Dan moeten we getters of setters gebruiken:

Deze update voegt deze beperking perfect in. Maar er is een probleem. Nu moeten de gebruikers van deze code al hun code updaten, en dat is niet altijd de bedoeling. Om de compatibiliteit te vrijwaren, kunnen we het property concept gebruiken.

In Python is de property functie een ingebouwde functie die dat een property object voor ons aanmaakt.

Nu kunnen we gelukkig terug car.speed gebruiken. Als we een waarde toekennen aan car.speed, dan wordt de setter set_speed uitgevoerd en als we er van lezen de get_speed. Zo moet dat.

Maar omdat python ook van leesbare code houdt, is er een decorator gemaakt die @property heet. Dit verbergt onze property functie voor ons en maakt van de functie een property object met dezelfde naam.

Maar we hadden hiervoor ook een setter. Ook dat bestaat, want de speed functie is nu een property en die heeft een setter.

Nu hebben we met deze code exact dezelfde functionaliteit als we willen zonder dat de oorspronkelijke interface gewijzigd is. En bovendien makkelijker te lezen als met de property functie.

List comprehensions

Ik ontdekte dat er mensen zijn die niet weten wat sommige syntax is van de python code die ik geschreven heb in mijn test framework. Een van de zaken die nog niet algemeen geweten is, zijn list comprehensions.

Als ik even zoek naar de python website zelf, dan zie ik daar wat zij beschouwen als list comprehension. Het is een bondige manier om lijsten aan te maken. Met een list comprehension moet je als programmeur dus niet altijd een for loop maken in een functie om een lijst aan te maken.

Een list comprehensions is een bondige manier om lijsten aan te maken.

Een voorbeeld zegt meer dan duizend woorden. Stel dat we een lijst willen hebben met getallen die met een vaste waarde worden verhoogd. We kunnen dit maken door de volgende functie:

Als we deze functie oproepen, krijgen we als resultaat een lijst:

Dit zelfde kunnen we vervangen door deze code:

Simpel, niet? Eens je de basis syntax van een list comprehension door hebt, is dit helemaal niet moeilijk meer.

Runtime Descriptors

In python kan je een klasse hebben met variablen. Dat is een van de basiszaken in een programmeertaal trouwens. Achter deze variablen kan je echter logica steken in python. Daarom is de property decorator uitgevonden.

Met deze properties, kan je dus in plaats van gewoon 1 waarde te geven, een actie doen of nakijken of de waardes voldoet aan de vereisten. Dit volstond voor mij niet, omdat ik de code voor deze properties graag in een andere klasse stak. Gelukkig hebben de ontwikkelaars van python daar al aan gedacht. De Descriptor heeft het ding. De syntax is:

Dit is dus de syntax van een property. Als we nu de property willen aanspreken, dan wordt de logica in de MyProperty classe opgeroepen. Dat is simpel, maar ik wou meer.

Ik wou dat deze properties dynamisch werden aangemaakt. Waarom? Omdat ik een beschrijvende file had gekregen waarin de API staat beschreven om onze backend aan te sturen. Als hieruit zelf code kon worden gegenereerd, moest ik niet al die functies uitschrijven en die gewoon gebruiken met een python achtige syntax. Daarom wou ik tijdens het runnen van mijn testen, die file inlezen en zo code genereren, die ik dan nadien zou kunnen oproepen.

Na lang zoeken, vond ik de manier om deze properties dynamisch te krijgen. Python heeft een functie setattr. Deze zit standaard in de taal en is de oplossing voor mijn probleem:

Op deze manier bevat de Container klasse properties die dynamisch aangemaakt kunnen worden en kan de backend op een manier aangesproken worden zonder er nog bij na te hoeven denken.

Mocht ooit een descriptor of property worden gedelete, dan is delattr ook te bekijken waard.