graphwalker

0

Описание

This is the repo for the Model-based testing tool GraphWalker

Языки

JavaScript

  • Java
  • ANTLR
  • HTML
  • CSS
Сообщить о нарушении
README.md

This is a fork of https://github.com/GraphWalker/graphwalker-project

Original documentation on http://graphwalker.org/

Added features

Here is description of added features.

Split into multiple graphs with the same context

The prehistory of this feature is as follows - in the library itself, it is already possible to split the test model into separate files, linking them together using the

SHARED
mechanism of steps. More information about this feature can be found on developers site. However, there are some drawbacks of using
SHARED
. For example, it is difficult to control
SHARED
tags consistency. If, by mistake, a transition to nowhere will be declared, it will be hard to debug such a model.

As a solution that added to the library's capabilities - new keywords were added to the syntax of model description -

INDEGREE
and
OUTDEGREE
. What does it mean:

  • Adding
    OUTDEGREE
    to the vertex means that there is a transition (edge) with the specified name from this vertex to some other vertex beyond the graph.
  • Adding the
    INDEGREE
    label to the vertex means the opposite, namely the existence of a transition (edge) to the current vertex from some external graph.

INDEGREE
and
OUTDEGREE
functionality provides us an ability to split huge test model graph into small, clear sub-graphs. When executing
mvn graphwalker: generate-sources
- it builds, a union of all the sub-graphs into one temporary *.graphml file and put it into the /link (resources) subdirectory. The union graph can be used:

  • to generate code interfaces to be implemented;
  • to check test model consistency;
  • to debug tests.

Test model, split into sub-graphs (vs the "monolithic" test model) has better maintainability. For example, in the case of a merge conflict, it will be much easier to resolve it in one small sub-graph file, rather than in one, huge, monolithic test model file.

Using

INDEGREE
we can implement some editional logics like:

INDEGREE: e_ClickHome /* Navigate home page */ [authorized==false], e_ClosePreview /* Close preview popup */;

In order not to duplicate the same command for a set of transitions to the same state, as well as to be able to specify similar commands for

OUTDEGREE → INDEGREE
connections, the keyword SET was introduced.

![SET example](docs/SET keyword.png?raw=true "SET keyword example")

Simplified timer integration into vertices

The template of the generated Java file has been changed. Generated interface methods based on vertices now return boolean value. Thus, any vertex-based method will run until the interface logic returns

true
or timed out.

Wait under the hood

@code annotation with YEd syntax

![@code annotation](docs/code annotation.png?raw=true "@code annotation")

In some cases, information about the generated element (edge or vertex), is enough to generate interface implementation code. Here's what the generated code will look like for the example above.

// Generated by GraphWalker (http://www.graphwalker.org)
package org.graphwalker.java.graphml;
import org.graphwalker.java.annotation.Model;
import org.graphwalker.java.annotation.Vertex;
import org.graphwalker.java.annotation.Edge;
import static org.graphwalker.java.annotation.Dataset.*;
@Model(file = "com/avito/graphwalker/model/CodeExample.graphml")
public interface CodeExample {
@Vertex(value = "@code isBrowserStarted(\"Firefox\")\\n browser started")
default boolean v_BrowserStarted() {
return isBrowserStarted("Firefox");
}
@Edge(value = "@code runBrowser(\"Firefox\")")
default void e_init() {
runBrowser("Firefox");
}
@Edge(value = "@code get(\\\"https://www.avito.com\\\");\\n Home page navigation\")")
default void e_navigate() {
get("https://www.avito.ru");
}
boolean isBrowserStarted(java.lang.String arg0);
void get(java.lang.String arg0);
void runBrowser(java.lang.String arg0);
@Vertex(value = "home page is open")
boolean v_OpenHome();
}

However, we should note that this feature is intended for generated models, not manually created.

RuleRight syntaxWrong syntax
only inside comment block
v_Vertex /* @code myCheck() */
@code myCheck() v_Vertex /* commentary */
semicolon to split
v_Vertex /* @code myCheck() any comment */
v_Vertex /* any comment @code myCheck() */
no logical operators on the top
v_Vertex /* @code myCheck("Текст") */
v_Vertex /* @code check1() && check2() */
only String, Number, Boolean...
v_Vertex /* @code myCheck("1", "2") */
v_Vertex /* @code myCheck(-"2") */
or other methods as parameters
e_Edge /* @code myAction((String)valueOf(1)); */
e_Edge /* @code myAction((Float)valueOf("1.0")); */
methods of the same names returns the same
v_Vertex /* @code myCheck() */
e_Edge /* @code myAction((Boolean)myCheck()); */
v_Vertex /* @code myCheck((Boolean)myAction())
*/ e_Edge /* @code myAction(); */
no other annotations
v_Vertex /* ***TODO*** */
v_Vertex /* @code */

Path generator with reachability validation

![Path generation](docs/Path generation.bmp?raw=true "path generation with reachability validation")

In the model above in order to get into

v7
, you need to set the necessary values of the guard variables
e5
,
e6
,
e7
. The only legal route to
v7
is green colored. To set such a route using AStarPath, we would have to describe an additional point
v1
, otherwise the wrong route
start → v07 → v7
would be generated, and will cause runtime exception.

val combinedPathGenerator = CombinedPath()
combinedPathGenerator.addPathGenerator(AStarPath(ReachedVertex("v1")))
combinedPathGenerator.addPathGenerator(AStarPath(ReachedVertex("v7")))

Using the

org.graphwalker.core.generator.ShortestPath
in path generation, it will be enough to specify only the final vertex.

val pathGenerator = ShortestPath(ReachedVertex("v7"))

It is important to note that generated paths can only contain unique vertices and edges, so a route like

A → B → A → C
(cycles or loops) will not be generated.

Factory method pattern to decouple implementation

Let's say there is a model made up of many individual sub-graphs.

class Model : ExecutionContext(),
EntryModel,
Attributes,
AddAttribute,
AddCategory,
Dependencies,
ChangeDependencyBasedOnAPI,
ChangeCustomDependency,
EditAttributeWithValueSelection,
EditGroupAttribute,
EditCategory,
EditField,
EditTextAttribute,
EditValue,
CreateDependency,
CreateDependencyBasedOnAPI,
CreateCustomDependency,
CreateValue,
CreateForm,
CreateStep,
Values,
ManageForm,
Forms,
AddBranch,
ChangeBranch,
DeleteValue,
DeleteBranch,
Vocabularies,
Categories,
ReleaseBranch
{ /* implementation goes here */ }

With implementations of that scale, it becomes more difficult to deal with name collisions as well as change individual parts. As an alternative to this solution, the ContextFactory interface will be generated in target / generated-sources, by implementing it, you can decouple sub-graph implementations.

class Model : ExecutionContext(), ContextFactory {
override fun getEntryModel(): EntryModel = EntryModel()
override fun getAttributes(): Attributes = Attributes()
/* and so on */
}

Parametrized tests

Parameterized tests can significantly reduce test model size and also make it much more readable. The area between two specially marked states will be replicated on as many separate automated tests as many records are in dataset. The parameterized automated tests themselves can be run in parallel with each other, thereby reducing the overall time.

Different types of parametrization

For parametrization both separate edges and parts of the graph built of several edges and vertices are available.

Parametrized test example

Syntax

To parameterize one single edge, you need to edit its label in the yEd editor. To do this, you can right-click on the selected edge and select "Add label". Then you need to insert the HTML table code. Unfortunately, yEd does not provide convenient ways to create such kind of tables, so you have to edit the HTML code in the editor itself. Read more about the features of yEd here and here. Table example can be taken from here and copy-pasted via the clipboard.

  • Dataset with two rows with
    label
    and
    s_trg
    parameters

Parametrized test example

<html>e_ClickTransport<br/>/* Navigate Transport */
<table>
<tr>
<th bgcolor="lime">label</th>
<th bgcolor="yellow">s_trg</th>
</tr>
<tr>
<td bgcolor="lime">Car</td>
<td bgcolor="yellow">6</td>
</tr>
<tr>
<td bgcolor="lime">"Truck"</td>
<td bgcolor="yellow">10</td>
</tr>
</table>
</html>
  • Dataset with a single parameter

Parametrized test example

<html> e_FillCredentials /* Fill credentials */
<table>
<tr bgcolor="lime">
<th>username</th>
</tr>
<tr bgcolor="yellow">
<td>admin</td>
</tr>
<tr bgcolor="orange">
<td>root</td>
</tr>
</table>
</html>
  • Multi-row dataset with
    label
    ,
    s_trg
    ,
    AB_test
    parameters

Parametrized test example

<html>e_ClickTransport<br/>/* @code clickLink(${label}, ${s_trg});
Navigate Transport */
<table>
<tr bgcolor="lime">
<th>label</th>
<th>s_trg</th>
<th>AB_test</th>
</tr>
<tr bgcolor="yellow">
<td>Car</td>
<td>6</td>
<td>false</td>
</tr>
<tr bgcolor="orange">
<td>Truck</td>
<td>10</td>
<td>false</td>
</tr>
</table>
</html>

Warning - String, Boolean, Numeric (int / double) data types are only supported. Only one type of data can be declared per column. The parameter of the String type can be declared as a single word or, if it consists of several words, quoted like -

"example test string with spaces"
.

In order to parametrize graph linear section (like octopus tentacle), it is necessary to connect two points of the section of the graph with the new edge. The starting point or root dataset is a vertex that will not be parameterized. The final is the vertex that will be parameterized. But at the same time, all transitions from that edge will no longer be parameterized.

Parametrized test example

The only difference between such a connecting edge and the previous version is that nothing is declared in it, except for the HTML table code. It contains neither the name of the edge, nor the text description, nor the weight or other parameters.

  • Dual-row dataset with
    label
    and
    s_trg
    parameters

Parametrized test example

<html>
<table>
<tr>
<th bgcolor="lime">label</th>
<th bgcolor="yellow">s_trg</th>
</tr>
<tr>
<td bgcolor="lime">Car</td>
<td bgcolor="yellow">6</td>
</tr>
<tr>
<td bgcolor="lime">"Truck"</td>
<td bgcolor="yellow">10</td>
</tr>
</table>
</html>
Generated code

For all parameterized elements - edges and vertices inside the section of the graph, including the final vertex, graphwalker library will generate methods like this

@Model(file = "my/company/graph/Model.graphml")
public interface Model {
@Edge(value = "@code clickLink(${label}, ${s_trg}); \nNavigate Transport")
@Row(value = { @Value(name = "label", value = "Car"), @Value(name = "s_trg", value = "6") })
@Row(value = { @Value(name = "label", value = "Truck"), @Value(name = "s_trg", value = "10") })
default void e_ClickTransport(java.lang.String label, double s_trg) {
clickLink(label, s_trg);
}
/* and so on */
}

Proper method arguments will be passed in runtime.

How to use

Run your datasets like this

// dataset parameter name
val name: String = "some name"
// number in range [0; datasetSize-1]
val id: Int = 0
// number of possible ways in dataset
val size: Int = 2
val dataset = Dataset(name, id, size)
val shortestPath = ShortestPath(ReachedVertex(groupName, vertexName), dataset)
Known restrictions
  • datasets nested into each other are prohibited
  • the parameterized part of the graph must not contain branches, including
    INDEGREE
    /
    OUTDEGREE
    labels.
  • the parameterized part of the graph should fit into one file, while there may be several such sections on one file, as well as several files with (different) datasets

Original authors

  • Nils Olsson
  • Kristian Karl

Modified code authors

Licence

MIT

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.