Skip to content

Writing_Tests

benoitgaudou edited this page Sep 1, 2019 · 10 revisions

Writing Unit Tests in GAML

Unit testing is an essential instrument to ensure the quality of any software and it has been implemented in GAMA: this allows in particular that parts of the model are behaving as expected and that evolutions in the model do not introduce unexpected changes. To these purposes, the modeler can define a set of assertions that will be tested. Before the execution of the embedded set of instructions, if a setup is defined in the species, model or experiment, it is executed. In a test, if one assertion fails, the evaluation of other assertions continue.

Writing tests in GAML involves the use of 4 keywords:

In this unit testing tutorial, we intend to show how to write unit tests in GAML using the statement test.

What is test in GAML?

In GAML, the statement test allows the modeler to write a part of code lines to verify if portions of our GAML model are doing exactly what they are expected to do: this is done through the use of several assertions (using assert statements). This is done independently from other parts of the model.

To write a typical GAML unit test, we can follow three steps:

  1. Define a set of attributes to use within the test,
  2. Write initialization instructions,
  3. Write assertions.

The aim of using unit testing is to observe the resulting behavior of some parts of our model. If the observed behavior is consistent with the expectations, the unit test passes, otherwise, it fails, indicating that there is a problem concerning the tested part of the model.

Introduction to assertions

The basis of Unit tests is to check that given pieces of codes provide expected results. To this purpose, the modeler can write some basic tests that should be true: s/he thus asserts that such expression can be evaluated to true using the assert statement. Here are some examples of assert uses:

assert 1 + 1 = 2;
assert isGreater(5, 6) = false;
assert rnd(1.0) <= 1.0;

With the above statements, the modeler states the 1+1 is equal to 2, isGreater(5,6) is false (given the fact that isGreater is an action defined in a species) and rnd(1.0) always returns a value below 1.0.

assert can be used in any behavior statement (as an example in a reflex, a state or in a test. Note that, if they are written outside of a test and that the test is not fulfilled, then an exception is thrown during their execution.

As an example, the following model throws the exception: Assert failed 3>4 (as obviously 3 is not greater than 4 and that the GAML > operator is properly implemented on this case).

model NewModel

global {
    init {
	assert 3 > 4;
    }
}

experiment NewModel type: gui {}

To be able to have a dashboard of the state of your model w.r.t. the unit tests, they need to be written in a test and the model launched with an experiment of type test.

How to write a GAML test?

A test statement can be used in any species (regular species, global or experiment species) everywhere a reflex can be used. Its aim is to gather several asserts in one block. If the tests are executed with any kind of experiment but test, they will be executed, but nothing is reported. With a test experiment, a kind of dashboard will be displayed.

So we will consider that we start by adding an experiment with type set to test. The following code shows an example.

experiment MyTest type: test autorun: true { 
    ...
}

Let's consider the following GAML code:

model TestModel

global {
    init {
	create test_agent number: 1;
    }
}

species test_agent {
    bool isGreater (int p1, int p2) {
	if (p1 >= p2) {
	    return true;
	} else {
	    return false;
	}
    }

    test testsOK {
	assert isGreater(5, 6) = false;
	assert isGreater(6, 5) = true;
    }
    
    test failingTests {
	assert ! isGreater(6, 6);
    }        
}

experiment MyTest type: test autorun: true { }

In this example, the defined action, isGreater, returns true if a parameter p1 is greater than a parameter p2 and false if not. So to test it, we declare a unit test using test and add inside several assert statements. For instance, assert isGreater(5, 6) = false; will return true if the result of isGreater(5, 6) is really false and false if not. So, if the action isGreater is well-defined, it should return false. Considering that "greater" and "greater and equal" should be two different functions, we add a test to check that isGreater does not return true in case of equality of its 2 operands. In this case, as the action is not-well implemented the test fails.

The following picture illustrates the GUI dashboard for unit tests, showing for each test and even each assert whether it passes or fails. Clicking on the button will display in the GAML editor the code line.

Interface for unit tests execution.

Use of the setup statement

In a species where we want to execute several tests, it is common to want to have the same initial states, in order to prevent the previous tests to have modified the tested object and thus altering the unit test results. To this purpose, we can add the setup statement in the species and use it to set the expected initial state of the object to be tested. It will be called before every test.

As an example, in the following model, we want to test the operator translated_by and translated_to on a point. As each of them will modify the point object to be tested, wed add a setup to reinitialize it.

model TestModel

global {
    geometry loc <- {0,0};
	
    setup {
	loc <- {0,0};	
    }

    test translate_to {
	loc <- loc translated_to {10,10};
	loc <- loc translated_to {10,10};
	assert loc.location = {10,10};
    }
	
    test translated_by {
	loc <- loc translated_by {10,10};
	loc <- loc translated_by {10,10};
	assert loc.location = {20,20};
    }
}

experiment MyTest type: test autorun: true { }

The test experiment

It is also possible to write tests in the experiment. The main idea is here to totally separate the model and its tests. As an example let's consider the following GAML code, which aims to test several GAML operators, related to the graph datatype:

model TestGraphs

global {
    graph the_graph;

    init {
	int i <- 10;
	create node_agent number: 7 {
	    location <- {i, i + ((i / 10) mod 2) * 10};
	    i <- i + 10;
	}

	the_graph <- as_distance_graph(node_agent, 30.0);
    }
}

species edge_agent {
    aspect default {
	draw shape color: #black;
    }
}

species node_agent {
    aspect default {
	draw circle(1) color: #red;
	loop neigh over: the_graph neighbors_of self {
	    draw line([self.location, agent(neigh).location]) color: #black;
	}
    }
}

experiment loadgraph type: gui {
    output {
	display map type: opengl {
	    species edge_agent;
	    species node_agent;
	}
    }
}

experiment MyTest type: test autorun: true {
    test "MyFirstTest" {
	write the_graph;
	write (node_agent[2]);
	write ("Degrees");
	write (the_graph in_degree_of (node_agent[2]));
	write (the_graph out_degree_of (node_agent[2]));
	write (the_graph degree_of (node_agent[2]));
	assert the_graph in_degree_of (node_agent[2]) = 4;
	write (the_graph out_degree_of (node_agent[2]));
	assert the_graph out_degree_of (node_agent[2]) = 4;
	assert the_graph degree_of (node_agent[2]) = 8;
    }
}
  1. What's new (Changelog)
  1. Installation and Launching
    1. Installation
    2. Launching GAMA
    3. Updating GAMA
    4. Installing Plugins
  2. Workspace, Projects and Models
    1. Navigating in the Workspace
    2. Changing Workspace
    3. Importing Models
  3. Editing Models
    1. GAML Editor (Generalities)
    2. GAML Editor Tools
    3. Validation of Models
  4. Running Experiments
    1. Launching Experiments
    2. Experiments User interface
    3. Controls of experiments
    4. Parameters view
    5. Inspectors and monitors
    6. Displays
    7. Batch Specific UI
    8. Errors View
  5. Running Headless
    1. Headless Batch
    2. Headless Server
    3. Headless Legacy
  6. Preferences
  7. Troubleshooting
  1. Introduction
    1. Start with GAML
    2. Organization of a Model
    3. Basic programming concepts in GAML
  2. Manipulate basic Species
  3. Global Species
    1. Regular Species
    2. Defining Actions and Behaviors
    3. Interaction between Agents
    4. Attaching Skills
    5. Inheritance
  4. Defining Advanced Species
    1. Grid Species
    2. Graph Species
    3. Mirror Species
    4. Multi-Level Architecture
  5. Defining GUI Experiment
    1. Defining Parameters
    2. Defining Displays Generalities
    3. Defining 3D Displays
    4. Defining Charts
    5. Defining Monitors and Inspectors
    6. Defining Export files
    7. Defining User Interaction
  6. Exploring Models
    1. Run Several Simulations
    2. Batch Experiments
    3. Exploration Methods
  7. Optimizing Model Section
    1. Runtime Concepts
    2. Optimizing Models
  8. Multi-Paradigm Modeling
    1. Control Architecture
    2. Defining Differential Equations
  1. Manipulate OSM Data
  2. Diffusion
  3. Using Database
  4. Using FIPA ACL
  5. Using BDI with BEN
  6. Using Driving Skill
  7. Manipulate dates
  8. Manipulate lights
  9. Using comodel
  10. Save and restore Simulations
  11. Using network
  12. Headless mode
  13. Using Headless
  14. Writing Unit Tests
  15. Ensure model's reproducibility
  16. Going further with extensions
    1. Calling R
    2. Using Graphical Editor
    3. Using Git from GAMA
  1. Built-in Species
  2. Built-in Skills
  3. Built-in Architecture
  4. Statements
  5. Data Type
  6. File Type
  7. Expressions
    1. Literals
    2. Units and Constants
    3. Pseudo Variables
    4. Variables And Attributes
    5. Operators [A-A]
    6. Operators [B-C]
    7. Operators [D-H]
    8. Operators [I-M]
    9. Operators [N-R]
    10. Operators [S-Z]
  8. Exhaustive list of GAMA Keywords
  1. Installing the GIT version
  2. Developing Extensions
    1. Developing Plugins
    2. Developing Skills
    3. Developing Statements
    4. Developing Operators
    5. Developing Types
    6. Developing Species
    7. Developing Control Architectures
    8. Index of annotations
  3. Introduction to GAMA Java API
    1. Architecture of GAMA
    2. IScope
  4. Using GAMA flags
  5. Creating a release of GAMA
  6. Documentation generation

  1. Predator Prey
  2. Road Traffic
  3. 3D Tutorial
  4. Incremental Model
  5. Luneray's flu
  6. BDI Agents

  1. Team
  2. Projects using GAMA
  3. Scientific References
  4. Training Sessions

Resources

  1. Videos
  2. Conferences
  3. Code Examples
  4. Pedagogical materials
Clone this wiki locally