Last week, this post described an example implementation of model-based testing, using a finite state machine to represent a web site, and randomly generated tests. The python code is open source, and available on gitHub.
Contains the class fsm, which implements the finite state machine. The fsm.mach attribute holds a representation of the machine in memory. There are 3 constructors, 2 for testing and 1 operational. The operational constructor takes a filename for the xml model, parses that file to read the model into memory.
The methods are used by the generate module to implement the test strategy. Several methods are used to get various attributes from the machine (the current state, next state, and list of possible transitions from the current state). The fsm.set_next_state() implements the transition. These methods are likely to be present in any finite state machine class.
One unique method is fsm.get_current_state_oracle(). This method is used by the generate module to create the success criteria of the test.
The generate module creates an instance of fsm, and implements a navigation strategy across the finite state model, and generates the test script. test_setup() writes the initialization code for a webdriver test program: imports webdriver and creates/initializes the driver object.
random_walk(m, n) takes a state machine, m, and an iteration count, n, as parameters. As the name implies, it walks through the state machine choosing a random transition at each state, and generates a test step that corresponds to the transition.
The basic test is: find the link represented by the next transition, click it, then verify that navigation was successful. The oracle, in this simple example, checks that the page title matches the oracle attribute from the model.
demo.py is the generated test file. This file is the one that is executed to implement the tests.
You’ll notice the fsm.py file has extensive unit tests. I used Test Driven Development for this project, where I wrote a test, then wrote the code necessary to pass the test. I found that TDD was an effective way to create the fsm class, implementing only the functionality needed. I could see that if I started with a pure specification of a finite state machine, I might have ended up with more code than required.
I did not use TDD for generate.py. In retrospect, one glaring difference is the lack of tests. I find that disturbing.