Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Concordia Compiler uses plug-ins for:
setting the testing environment up;
generating and executing test scripts;
converting and reporting test results.
Every plug-in can generate test scripts for a different testing framework or programming language.
Name (concordialang-...)
Target platform
Needs Java?
RTS*
web
No
No
web
No
No
web
Yes, Java 8+
Yes
Android, iOS, Windows
It depends**
Yes
Notes:
(*) RTS means "Requires a Testing Server", that is, if it requires a testing server to run the test scripts (e.g., Selenium Standalone).
(**) Appium requires Java for testing web-based mobile applications or using the Android SDK.
codeceptjs-testcafe uses the frameworks CodeceptJS and TestCafé, and works with probably any browser.
codeceptjs-playwright uses the frameworks CodeceptJS and Playwright,
requires Node 10.14 or above, and works with Chromium, Firefox, and Safari.
codeceptjs-webdriverio uses the frameworks CodeceptJS and WebDriverIO, and works with Chrome, Firefox, IE, and Edge.
codeceptjs-appium uses the frameworks CodeceptJS and Appium, and requires some additional configuration to work.
codeceptjs-testcafe and codeceptjs-playwright are only available for Concordia Compiler 2 or above.
Use --plugin-install plus the plug-in name. Example:
npx concordia --plugin-install codeceptjs-testcafeJust uninstall the plug-in and then install it again. Example:
npx concordia --plugin-uninstall codeceptjs-playwright
npx concordia --plugin-install codeceptjs-playwright Concordia has the following plug-in commands:
plugin-install to install a plug-in
plugin-uninstallto uninstall a plug-in
plugin-serve to start a testing server using the plug-in
plugin to use a plug-in
plugin-info to show information about a plug-in
plugin-list to list installed plug-ins
All but the latter command (plugin-list) require a plug-in name.
👉 Whether you have a configuration file with the property plugin defined, you can omit the plugin name from a command. Example:
{
"plugin": "codeceptjs-playwright"
}
You can omit the argument <plugin> if you have a configuration file with the property "plugin" defined.
Some plug-ins (WebDriverIO, Appium) require a testing server to execute test scripts. A testing server controls a browser or an emulator during tests.
We recommend to open a new terminal/console and then start the testing server:
npx concordia --plugin-serve <plugin>The testing server will remain open. To stop it later, type Ctrl + C.
npx concordia --plugin <plugin>You can use --no-run to avoid running test scripts and use --no-result to avoid reading the last report with test results. Test scripts will be generated but not executed.
npx concordia --plugin <plugin> --no-run --no-resultYou can use --no-script to avoid generating test scripts. Only existing test scripts will be executed.
npx concordia --plugin <plugin> --no-scriptWelcome to the Concordia 2 documentation! This page will help you to get started fast. If you run into problems, you can find help on our Slack channel.
Concordia Compiler requires only NodeJS version 12.20 (or above) and works on Linux, MacOS and Windows.
In this Quick Start, we'll create a simple specification of an interaction with a search page and use Google Chrome. Therefore, download it before continuing.
If you are starting a new project or whether your project does not have a file named package.json, then run:
npm init --yesInstall Concordia Compiler locally:
npm i -D concordialangIt will install version 2 that is in alpha stage, although stable.
Note that the needed package name is concordialang (with lang). After installing it, we'll always use npx concordia to execute it.
npx concordia --initConcordia Compiler will ask you about some preferences. Press Enter to answer every question with their default values. It will create a configuration file named .concordiarc and install the selected plug-in and database drivers.
Create the folderfeatures :
mkdir featuresNow create the file search.feature withinfeatures, with the following content:
Feature: Search
Scenario: Shows results that correspond to the term
Variant: Search by pressing Enter
Given that I am on "https://google.com"
When I type "concordialang" in <q>
And I press "Enter"
And I wait for 2 seconds
Then I see "npm"About the file:
Feature and Scenario are high-level, business-focused descriptions about the problem to solve. Their sentences are not used to generate test cases. The above example does not describe them.
A Variant describes the expected interaction with the application's user interface (UI) in order to perform a Scenario. Thus, a Variant uses technological vocabulary.
In Concordia, all the interactions with the UI use first person singular ("I"). That "I" represents the actor that is interacting with the application (in the example above, a visitor).
Finally, run
npx concordia✨ That's it. Congratulations!✨
It will:
set the testing environment up (once);
generate a test case file and transformed it into a test script file;
execute the test script file; and
report the test script results.
Your browser should open automatically during this process and the console will report the execution results.
Your directory should now look like this:
┃
┣ features/
┃ ┣ search.feature
┃ ┗ search.testcase 🡐 generated test case
┣ node_modules/
┣ test/
┃ ┗ search.js 🡐 generated test script
┣ .concordiarc
┣ codecept.json
┣ package.json
┗ package-lock.jsonThe directory node_modules contains installed dependencies, like tools and frameworks, whose versions are managed with package.json and package-lock.json. The file codecept.json has a basic configuration to run CodeceptJS with Google Chrome and you can change it to fit your needs.
The file features/search.testcase should look like this:
# Generated with ❤ by Concordia
#
# THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
import "search.feature"
@generated
@scenario(1)
@variant(1)
Test Case: Search by pressing Enter - 1
Given that I am on "https://google.com"
When I type "concordialang.org" in <q>
And I press "Enter"
And I wait for 2 seconds
Then I see "npm"The Test Case above was produced from the Variant declared in features/search.feature. Some notes about it:
The import clause (line 5) imports the declared file's content.
The tag @generated (line 7) indicates that the Test Case was produced automatically.
The tag @scenario(1) (line 8) indicates that the Test Case belongs to the first Scenario (1).
The tag @variant(1) (line 9) indicates that the Test Case belongs to the first Variant (1) of the Scenario declared previously.
The Test Case (line 10) is named using the Variant's name plus dash (-) and some (incremental) number. Its content is basically the same as the Variant's, since we did not use any other declarations.
The file test/search.js contains the test script produced from features/search.testcase using the selected plug-in. It also contains line comments with references to their corresponding lines and columns in the .testcase file:
// Generated with ❤ by Concordia
// source: search.testcase
//
// THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
Feature("Search");
Scenario("Shows results that correspond to the term | Search by pressing Enter - 1", (I) => {
I.amOnPage("https://google.com"); // (11,2)
I.fillField("q", "concordialang.org"); // (12,2)
I.pressKey("Enter"); // (13,4)
I.wait(2); // (14,4)
I.see("npm"); // (15,2)
});
You can opt to install Concordia Compiler locally (per project) or globally.
does not require administrative privileges (i.e., using sudo on Linux or MacOS) to install;
allows every project to have its own, independent installation;
demands using NPX before every command.
Follow the Quick Start for a install installation.
Global installation
requires administrative privilegies (i.e., using sudo on Linux or MacOS) to install;
lets you execute the compiler direcly from any folder;
needs more attention when upgrading, especially for plug-ins.
Example:
sudo npm install -g concordialang
concordia --versionNote: On Windows, you must omit sudo.
Additional tips:
Whether Windows requires administrative privileges to install, right click Windows (Start) menu, click "Command Prompt (Admin)" or alternatively "Windows PowerShell (Admin)", and then proceed with the installation command.
To upgrade Concordia later, change the installation command from install to upgrade.
Concordia can use databases to retrieve test data or to set the testing environment up before or after running test scripts.
Use --db-install plus the driver name. Example:
npx concordia --db-install mysqlAvailable driver names:
Driver Name
Database
note
csv
CSV files
xlsx
Excel files
firebase
Firebase
ini
INI files
json
JSON files
Already installed
mysql
MySQL
adodb
MS Access
Windows only
mssql
MS SQL Server
postgres
PostgreSQL
sqlite
SQLite
Database drivers are installed as development dependencies of your application.
Use --db-uninstall plus the driver name. Example:
npx concordia --db-uninstall mysqlJust uninstall and then install it again.
A Database is global declaration. You must declare it once and then import its file in other features. Example:
Database: My DB
- type is "mysql"
- host is "127.0.0.1"
- name is "acme"
- username is "tester"
- password is "secret"
Database: Another DB
- type is "json"
- path is "./db.json"A database can be referenced inside UI properties and Test Events by its name between [ and ]. Example: [My DB]. To reference a database table, separate the table name from the database name with with a dot (.) - e.g. [My DB.profession]. Let's see an example:
import "db.feature"
# ... other declarations here ...
UI Element: Profession
- value comes from "SELECT name FROM [My DB.profession]"
Otherwise, I see "Profession not found."
- required
Otherwise, I see "Please select the profession."
Before Each Scenario:
Given that I connect to [My DB]
When I run the script 'DELETE FROM profession'
and I run the script 'INSERT INTO profession (name) VALUES ("Enginner"), ("Accountant")'
Instead of reinventing the wheel, Concordia uses SQL to query and modify databases and files. Even JSON and INI files can be queried with SQL.
In the example above, the property value from the UI Element "Profession" indicates that its (valid) values are retrieved from the table "profession" that belongs to the database declared as "My DB". The test event Before Each Scenario was declared to connect to the database, clean the table "profession", and then insert new records that the UI Element "Profession" will use.
Imagine a solution in which you can write software requirements using an Agile-style, restricted natural language and it automatically generates functional test scripts for you using effective testing techniques such as equivalence partitioning, boundary-value analysis, random testing and combinatorial testing and it can also use test data from databases and files. Well, that's Concordia. Its solution is composed by a metalanguage and a compiler.
Concordia metalanguage allows you to focus on writing your application's business rules and defining its expected behavior for when users interact with it. Stakeholders, business analysts, testers, and programmers can now discuss the application using a single language, a single source of truth. Business-focused and testing-focused declarations are clearly separated, so you can discuss technological details only with the interested parties.
With Concordia, you can write both functional and non-functional requirements, although it produces only functional test cases. Because it adopts Agile-style declarations, you don't have to write it so formally (like Use Cases usually do) and you can use plain text - which is easy to evolve with your codebase.
We invite you to take a look at the language and see how it is easy to understand. It is currently available in English (en) and Portuguese (pt).
Concordia compiler mix compiler techniques with machine learning and natural language processing to understand declarations in Concordia Language - which uses restricted natural language. After detecting declarations, it infers test scenarios, test data, and test oracles to generate test cases in a framework-independent format (see the produced test cases). Finally, it uses a plug-in created for a specific testing framework to generate test scripts (that is, source code), execute them and read their results. You don't need to write code. And the compiler checks your declarations for logic errors and other possible problems.
Follow the Quick Start to see it in action! 😉
Concordia compiler uses plug-ins to transform Abstract Test Cases (ATS) into test scripts - that is, into source code. Every plug-in can be implemented to generate source code for a specific programming language and testing framework - in Java, Python, Go, PHP or whatever the language you prefer. The generated source code is not tied to JavaScript or TypeScript.
See the available plug-ins and how to create a plug-in for your favorite programming language and testing framework.
Overview of the Concordia declarations:
A Featuredefines a desired behavior for the software, a piece of functionality that gives some business value. A Scenario is a high-level usage scenario of a Feature. A Feature can have many Scenarios.
Since Feature and Scenario are business-oriented declarations, they are not used by Concordia Compiler to generate test cases. You can write them freely. Although, we recommend you to use the User Story template for a Feature and the Given-When-Then template for a Scenario. Example:
Feature: Sales Report
As a manager
I would like to generate a sales report
In order to track company sales
Scenario: Daily sales report
Given that I am authenticated as a manager
When I open the sales report
and I choose the option "Daily report"
Then I see a list with all the today's sales
and the total valueThis is the User Story template:
As a <role>
I would like to <goal to perform>
In order to <received benefit> (or So that <received benefit>)
And this is the Given-When-Then (GWT) template:
Given <some context or precondition>
When <some action is carried out>
Then <a result or postcondition is observed>
In the GWT template, additional sentences of each kind are written in the next line and receive the prefix and.
Features and Scenarios must have a name, but their description are not required. Thus, you can write the above example like this (although we recommend against):
Feature: Sales Report
Scenario: Daily sales reportFeature files receive the extension .feature. Only one feature per file is allowed.
A Variant is a test-oriented declaration that describes a possible interaction between a user and the system in order to accomplish a Scenario. You can write more than one Variantfor a same Scenario .
A Variant must use the GWT template and first person singular ("I") in its sentences. Concordia Compiler uses Natural Language Processing (NLP) techniques to understand Variant sentences, aiming to transform them into test cases. Example:
Feature: New User
Scenario: Successful registration
Variant: Register with a new account
Given that I am on "https://example.com/register"
When I fill <#name> with "Bob"
and I fill <#dateOfBirth> with "11/30/1990"
and I enter with "bob@example.com" in <#email>
and I inform "m123456" in <#pass>
and I type "m123456" in <#confirmation>
and I click <#ok>
Then I see "Welcome, Bob" Concordia Compiler understands the sentences from line 7 to11 as being variations of the same action, fill.
Of course, the vocabulary is limited. Although, you can extend it whether you need (by adding new synonyms to a specific dictionary file). Actions and common variations are documented and suggestions are always welcomed.
In the sentences above, the values were denoted between quotation marks, e.g. "bob@example.com". Numeric values are also accepted and do not need quotation marks. Example: 97.
Widgets (of the user interface) are written between < and >. Example: <#email>. Concordia adopts the well-known CSS query selectors to locate widgets, regardless the user interface (its plugins convert them to the format adopted by the testing framework). You can also use XPath (although some plug-ins may not accept it). Examples:
# (hashtag) to find a widget by its id. Example: #email
. (dot) to find a widget by its class, if applicable. Example: .colorful
@ (at) to find a widget by its name. Example: @pass
~ (tilde) to find a widget by its mobile name, if applicable. Example: ~pass
no prefixed characters, to find a widget by its type. Example: input
// (double slash) to find a widget by its XPath. Example: //table/tbody/tr[1]
Whether you are testing a web application, you may find Katalon-Concordia useful. It's a browser extension - available for Google Chrome and Mozilla Firefox - that extends Katalon Recorder to save a recorded interaction as Variant sentences. Using it may reduce the time needed to locate the widgets of an existing application.
A State is denoted by a text between tilde (~), such as ~a state~, and must be written inside Variant sentences only. Example:
Then I have a ~user logged in~A Variant can both produce or require states. A State is produced by a Then sentence, like in the prior example. A State is required by a Given or a When sentence. For instance:
Given that I have ~user logged in~By declaring a required State, you're establishing a dependency among the current Variant and the ones that can produce it. Since that State is often produced by a Variant from another Feature, you have to import the corresponding Feature file. Concordia Compiler will only search for states from imported Feature files.
When transforming Variants into Test Cases, Concordia Compiler will:
Remove any (Then) sentences with a produced State; and
Replace any (Given or When) sentences with a required State by sentences from the Variant that can produce it.
Whether there are different Variants that can produce a (same) required State, Concordia Compiler will be able to combine each of them with the current Variant, aiming to produce Test Cases that explore different execution paths. For more information, please read the section State Based-Combination.
Let's say that we have a simple Login feature like this:
Feature: Login
Scenario: Sucessful login
Variant: Login with username and password
Given that I am on the [Login Screen]
When I enter with "bob" in {Username}
and I enter with "123456" in {Password}
and I click on {OK}
Then I see "Welcome"
and I have ~user logged in~
Constants:
- "Login Screen" is "/page"
UI Element: Username
UI Element: Password
UI Element: OK
- type is buttonNow let's suppose that a user has to be logged in to access its account details:
import "login.feature"
Feature: My Account
Scenario: See my account data
Variant: Show basic data by default
Given that I have ~user logged in~
and I am on the [Account Screen]
Then I see {User} with "bob"
and I see {Name} with "Robert Downey Jr"
Constants:
- "Account Screen" is "/account"
UI Element: UserConcordia Compiler will produce my-account.testcase with a Test Case like the following, that includes steps from both the features:
import "my-account.feature"
@scenario(1)
@variant(1)
Test Case: See my account data - 1
Given that I am on the "/login"
When I enter with "bob" in <username>
and I enter with "123456" in <password>
and I click on <ok>
Then I see "Welcome"
Given that I am on the "/account
Then I see <user> with "bob"
and I see <name> with "Robert Downey Jr"A Test Case represents a test case produced for a certain Variant . Concordia Compiler can generate many Test Case declarations from a same Variant, each one covering different states, input combinations and oracles. The generation considers the following declarations:
Variant
states
UI Element
Constants
Table
Database
The simplest Test Case is one whose sentences are exactly equal to the Variant's. That's the case when the Variant does not use other declarations and it explicits all the input test data to use.
For comparison, whether we simply remove with "Bob"from the sentenceWhen I fill <#name> with "Bob" , Concordia Compiler will generate a pseudo-random value to complete the sentence, and it will be like this: When I fill <#name> with "A~!39esljdns so2u1 *ns%".
Concordia Compiler uses a single seed to generate pseudo-random numbers in its algorithms and values. Everytime it runs, a new seed is produced. You can set the seed using the CLI parameter --seed. By setting the seed, the compiler will pick the same paths and produce the same values - which is desirable for reproducing the same behavior in the application under test, but not for discovering new defects.
A UI Element represents a user interface element and can denote its expected behavior through properties.
Although a Widget is easy to declare, it can have some shortcomings:
1) It can make a sentence hard to read, specially when it uses XPath or CSS locators.
2. It can be hard to maintain. When a Widget appears in more than one Scenario and its locator needs to change (e.g. you have changed it in the UI), you need to search and replace all its occurrences in the document. With a UI Element, you just need to change the property locator.
3. It does not offer resources for test generation other than its locator.
A UI Element offers properties that can describe the behavior related to it. These properties are used to infer the test data and test oracles to generate.
Example:
UI Element: Net Price
- minimum value is 10.00
Otherwise I see "Net Price must be greater than U$ 10" These are the UI Element properties:
id or locator ⇨ how to locate the widget
type ⇨ widget type, like textbox, button, etc.
editable ⇨ whether the UI Element is editable (or not)
data type ⇨ data type of an editable UI Element
required⇨ whether the UI Element is required (or not)
format⇨ data format, denoted by a regular expression
value⇨ defines how the value is produced
minimum value ⇨ defines how the minimum value is produced
maximum value ⇨ defines how the maximum value is produced
minimum length ⇨ defines how the minimum length is produced
maximum length ⇨ defines how the maximum length is produced
By default, declaring a UI Element without properties is the same as declaring it with:
id or locator equal to the UI Element name in camelCase (e.g., "Net Price" becomes "netPrice"). The adopted case is configurable.
type equal to textbox
data type equal to string
Another example:
UI Element: Profession
- value is in [ "Programmer", "Tester", "Analyst" ]Please see the language reference for more details. Let's proceed with the other declarations for now.
A Import declaration allows you to import declarations from another .featurefile. Example:
Import "login.feature"Imported declarations are Feature, Constants, Table, Database, and UI Element.
A Constants block defines one or more constants. Every Constant holds an immutable value that is accessible through the declared name. Example:
Constants:
- "Register Page" is "https://example.com/register"
- "Coupon Number" is 12345Constants are accessed by their name between [ and ]. Example:
Given that I am on the [Register Page]
When I fill {Coupon} with [Coupon Number]Constants are global declarations and cannot have duplicate names. You can use them in UI Element properties and Variant sentences.
A Table declares values in rows and columns to use in UI Element properties. Example:
Table: Customer
| name | age |
| Ada Lovelace | 36 |
| Dennis Ritchie | 70 |
| Donald Knuth | 82 |
| George Boole | 49 |
| Niklaus Wirth | 83 |
UI Element: Name
- value comes from "SELECT name FROM [Customer]"
Otherwise I see "Customer not found."Note that SQL is used to select values from the declared table, which is denoted between [ and ] (like Constants). Table names are global and share the same namespace as Constant names.
A Database provides a way to indicate a connection to an existing database or file. Example:
Database: AcmeDB
- type is "mysql"
- name is "acme"
- host is "http://127.0.0.1"
- username is "tester"
- password is "testing123"
UI Element: Name
- value comes from "SELECT name FROM [AcmeDB.customer]"
Otherwise I see "Customer not found."SQL is used to select values from a table or view from the declared database, which is denoted between [ and ] . Its table or view names are separated by a dot (.). Database names are global and share the same namespace as Tables and Constants.
In order to set the execution environment up, testers can define events that can run database commands or command-line scripts. These events can occur before or after Scenarios or Features:
Before Each Scenario: executes before every test of each Scenario of the current Feature.
After Each Scenario: executes after every test of each Scenario of the current Feature.
Before Feature: executes once, before all the tests of the current Feature.
After Feature: executes once, after all the tests of the current Feature.
Before All: executes once, before all the tests of the application under test.
After All: executes once, after all the tests of the application under test.
Test Events are written like Variants, using Given-When-Then. Since they usually perform actions, it is very likely to useWhen in most sentences. Example:
Before Feature:
Given that I connect to [AcmeDB]
When I run the script 'DELETE FROM customer'
and I run the script 'INSERT INTO customer ( name, age ) VALUES ( "Alice", 20 ), ( "Bob", 30 )'
and I run the command 'rmdir log'Commands and database scripts are denoted between apostrophe (').
This example covers the generation of test data and oracles and the combination of features using states.
Let's suppose that we have to create two simple features for a web application: "Login" and "Add Product". After discussing them with stakeholders and the team, we created the following Concordia specification (see the two tabs):
Feature: Login
As a user
I would like to authenticate myself
In order to access the application
Scenario: Successful login
Given that I can see the login screen
When I enter with valid credentials
Then I can access the application's main screen
Variant: Login with username and password
Given that I visit the [Login Screen]
When I fill {Username}
And I fill {Password}
And I click on {OK}
Then I see "Welcome"
And I have a ~user logged in~
Table: Users
| username | password |
| bob | 123456 |
| alice | 4l1c3pass |
UI Element: Username
- required
Otherwise I must see "Please inform the username."
- value comes from "SELECT username FROM [Users]"
Otherwise I must see "Invalid username."
UI Element: Password
- required
Otherwise I must see "Please inform the password."
- value comes from "SELECT password FROM [Users] WHERE username = {Username}"
Otherwise I must see "Invalid password."
UI Element: OK
- type is button
Constants:
- "Login Screen" is "/login"
import "login.feature"
Feature: Add Product
As an employee
I would like to add a product
In order to manage its data and sale it
Scenario: Add a new product with basic data
Given that I am logged in
When I add a product with SKU, description, department, price, and quantity
Then I am able to register it
Variant: Basic data
Given that I have a ~user logged in~
and I visit the [Product Screen]
When I fill {SKU}
and I fill {Description}
and I fill {Price}
and I fill {Quantity}
and I click on {Save}
Then I have ~product registered~
and I see "Saved."
UI Element: SKU
- required
Otherwise I see "Please inform the SKU."
- format is "/[A-Z]{3}\-[0-9]{3}/"
Otherwise I see "Invalid SKU format. Please use 3 letters, dash, 3 numbers."
UI Element: Description
- minimum length is 2
Otherwise I see "Description needs at least 2 characters."
- maximum length is 100
Otherwise I see "Description must have at most 100 characters."
UI Element: Price
- required
Otherwise I see "Please inform the price."
- minimum value is 0.01
Otherwise I see "Minimum price is one cent."
UI Element: Quantity
- data type is integer
UI Element: Save
- type is button
Constants:
"Product Screen" is "/products/new"
These features could correspond to the following sketches:
The specification helps stakeholders, analysis, testers and developers to create a shared understanding about the application and its business rules. It helps with the requirements validation, knowledge documentation, project design, implementation and testing.
Different from Use Cases and other requirements specification formats, with Concordia you probably don't need to define different scenarios for validation. Instead of having to manually define a set scenarios for validation, you just need to define how the UI elements should behavior - in their declaration - and the compiler will generate different scenarios for you in the form of Test Cases. Whether your application has a lot of such rules, your team will certainly benefit from it.
<TO-DO (UNDER CONSTRUCTION)>
Remember that a Variant express a possible interaction between a user and the application in order to complete its (business-focused) Scenario. lt follows the GWT syntax and always uses the word "I" to represent the user or user role denoted in the Feature's description.
Let's run Concordia Compiler with a seed to produce the same results over and over again:
npx concordia --seed="example" --no-run --no-resultIt will generate login.testcase with the following content:
<TO-DO (UNDER CONSTRUCTION)><TO-DO (UNDER CONSTRUCTION)>If a seed is not given, Concordia Compiler assumes the current date and time. The seed is always printed in the console so that you or your team can reproduce the same paths, test data, and test oracle that were able to expose a bug in your application. Here we are giving it for a sake of reproduction.
Using the same seed over and over again will make Concordia Compiler produce the same results. Options no-run and no-result avoid running the test scripts and getting execution results.
This will not generate test cases or test scripts, but it will execute them and get their results:
$ concordia --plugin=codeceptjs --no-test-case --no-scriptlogin.testcase:
# Generated with ❤ by Concordia
#
# THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
import "login-en.feature"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 1
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "*RM)O," # invalid: inexistent element
And i fill <password> with "" # valid: last element
And I click on <ok> # {OK}
Then I must see "Invalid username" # from <username>
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 2
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "bob" # valid: filled
And i fill <password> with -8655972838932479 # invalid: inexistent element
And I click on <ok> # {OK}
Then I must see "Invalid password" # from <password>
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 3
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "alice" # valid: random element
And i fill <password> with "4l1c3pass" # valid: random element
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 4
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "alice" # valid: last element
And i fill <password> with "4l1c3pass" # valid: filled
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@fail
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 5
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "" # invalid: not filled
And i fill <password> with "" # invalid: not filled
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 6
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "bob" # valid: first element
And i fill <password> with 123456 # valid: first element
And I click on <ok> # {OK}
Then I see "Welcome"test/login.js:
// Generated with ❤ by Concordia
// source: c:\code\tmp\concordia-test-pt\login-en.testcase
//
// THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
Feature("Login");
Scenario("Successful login | Successful login with valid credentials - 1", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "*RMO,"); // (11,5) invalid: inexistent element
I.fillField("password", ""); // (12,7) valid: last element
I.click("ok"); // (64,7) {OK}
I.see("Invalid username"); // (14,5) from <username>
});
Scenario("Successful login | Successful login with valid credentials - 2", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "bob"); // (21,5) valid: filled
I.fillField("password", "-8655972838932479"); // (22,7) invalid: inexistent element
I.click("ok"); // (64,7) {OK}
I.see("Invalid password"); // (24,5) from <password>
});
Scenario("Successful login | Successful login with valid credentials - 3", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "alice"); // (31,5) valid: random element
I.fillField("password", "4l1c3pass"); // (32,7) valid: random element
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 4", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "alice"); // (41,5) valid: last element
I.fillField("password", "4l1c3pass"); // (42,7) valid: filled
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 5", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", ""); // (52,5) invalid: not filled
I.fillField("password", ""); // (53,7) invalid: not filled
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 6", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "bob"); // (62,5) valid: first element
I.fillField("password", "123456"); // (63,7) valid: first element
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});Whether you ran the test scripts above, you probably saw they fail. That's because they didn't find a web application running at http://localhost/login or because the application was found but it did not match the expected behavior. You may adapt your application and run the tests again.
Concordia shows a report that indicates the failures' locations. They help you to decide if a failure was caused by the application under test (e.g., it did not behave as expected) or because of the requirements specification (e.g., it is outdated in relation to the application).
Now keeping your specification updated has a new clear benefit: you can use it to generate tests and discover existing defects in your application!
To cite Concordia in a paper, please cite its PhD Thesis:
Concordia Compiler infers test cases, test data and test oracles from specification files written in Concordia and tries to cover a large number of paths that can expose defects in your application. Its approach keeps the specification relevant, up-to-date and more useful for stakeholders, testers, and developers than other documentation formats.
We recommend the following usage cycle:
Write or update your requirements specification with the Concordia Language and validate them with users or stakeholders;
Use Concordia Compiler for generating functional tests from the specification and running them;
If the tests failed, there are some possibilities:
a) You still haven't implemented the corresponding behavior in your application. In this case, just implement it and run the tests again.
b) Your application is behaving differently from the specification. In this case, it may have bugs or you or your team haven't implemented the behavior exactly like described in the specification.
Whether the application has a bug, we are happy to have discovered it! Just fix it and run the tests again to make sure that the bug is gone.
Otherwise, you can decide between changing your application to behave exactly like the specification describes, or changing the specification to match your application behavior. In the latter case, back to step 1.
If the tests passed, great job! Now you can write new requirements or add more test cases, so just back to step 1.
When you type concordia, the compiler performs the following process:
It reads your .feature and .testcase files, and uses a and a to identify and check documents' structure.
It uses (NLP) to identify sentences' , which increases the chances of recognizing different writing styles.
It performs to check recognized declarations.
It uses the specification to infer the most suitable test cases, test data, and test oracles, and then generates .testcase files in Concordia Language.
It transforms all the test cases into test scripts (that is, source code) using a plug-in.
It executes the test scripts with the plug-in. These test scripts will check your application's behavior through its user interface.
It reads and presents execution results. These results relate failing tests to the specification, in order to help you understanding the possible reasons of a failure.
Abstract actions recognized through Natural Language Processing
Concordia Language contains a set of meta-actions that can be used by different testing frameworks for generating test scripts. Concordia Compiler uses Intent Recognition (yes, like chatbots do) to understand sentences and extract the desired action to perform. Later these actions are sent to testing frameworks for conversion into commands.
The following list presents the available actions and gives some examples on how to use them in sentences. We expect that you do not have to memorize them but, instead, see the things you can do. Try to write the sentences the way you speak, using first person singular (I) . When there is a UI Element in the sentence, embrace it with { and }, like {this}. The compiler will tell you whether it cannot understand some sentence you wrote. If you think it should have understood something, . We'll try to augment the compiler's vocabulary to understand it the next time.
acceptAccepts a browser message or app message.
amOnIndicates a webpage or screen in which it is expected to be at.
appendAdds a value to an element.
attachFileAttaches a file. It selects a file and confirms its choice (e.g., clicks Open).
cancelCancels a browser message or app message.
checkChecks a checkbox.
clearEmpties an input field or browser cookie
clickClicks on something in the screen.
closeCloses a tab, a set of tabs, or an app (mobile only).
connectConnects to a database.
The next sentence is for only:
disconnectDisconnects from a database.
The next sentence is for only:
doubleClickPerforms a double click on something.
dragDrags and drops something to a widget.
fillIndicates that a field is being filled. If desired, a value can be given. Otherwise, a value will be generated for the corresponding Test Case.
hideHides something.
installInstalls an app.
maximizeMaximizes a window or the browser.
moveMoves the mouse cursor to a place or element.
openOpens something
pressPresses a key or key combination, separated by comma.
Some special keys (case sensitive!):
Add
Alt
ArrowDown or Down arrow
ArrowLeft or Left arrow
ArrowRight or Right arrow
ArrowUp or Up arrow
Backspace
Command
Control
Del
Divide
End
Enter
Equals
Escape
F1 to F12
Home
Insert
Meta
Multiply
Numpad 0 to Numpad 9
Pause
Pagedown or PageDown
Pageup or PageUp
Semicolon
Shift
Space
Subtract
Tab
pullExtracts a device's resource from a path.
refreshRefreshes/reloads the current page.
removeRemoves/uninstall an app by its internal name (mobile only).
resizeResizes a window.
rightClickPerforms a right click on something.
runRuns a console command or a database script (SQL command).
👉 Available for only.
👉 Commands and SQL scripts must be declared between single quotes ('), in a single line.
👉 SQL scripts must reference their database.
Concordia Compiler uses to access databases and files through SQL-like commands. The supported SQL syntax may vary from one database to another. Please see the .
JSON and CSV as databases: INSERT accepts JSON objects or arrays as values. Example:
Excel and Firebase databases: Syntax similar to JSON and CSV databases. However, it has some limitations, as pointed out in :
SQL commands are limited to SELECT, UPDATE, INSERT and DELETE. WHERE works well. JOINs are not allowed. GROUP BY is not supported. LIMIT and OFFSET are combined into a single LIMIT syntax: LIMIT [offset,]number
INI databases: INSERT and DELETE are not supported yet by . UPDATE example:
SQLite databases: Currently uses that doesn't persist the changes in the file (only in memory).
saveScreenshotTakes a screenshot an saves into a file.
scrollToScrolls to a certain element.
seeIndicates that something can be seen. You can also negate the sentence to indicate something cannot be seen.
selectSelects a value for an element.
shakeShakes the device - MOBILE ONLY.
swipePerforms a swipe action - MOBILE ONLY
switch
Switches to a certain iframe and back to the application page; OR
Switches to a certain tab; OR
Switches an app to native mode or web mode - MOBILE ONLY.
NOTE: When switching to an iframe, all the commands that follow it will be applied to it.
tapPerforms a tap on an element - MOBILE ONLY
uncheckUnchecks a checkbox.
waitWait for something.
Time is a crucial constraint in software development and software teams often need to focus their test efforts on the most important application paths.
⚠ UNDER CONSTRUCTION ⚠
In software testing, test coverage is some kind of metric that helps you to understand what parts of your application (or code) are exercised by your tests. The number of feasible paths through them grows exponentially with an increase in application size and can even be infinite in the case of applications with unbounded loop iterations. That is a problem called path explosion problem. Concordia Compiler deals with it by providing sets of combination and selection strategies, and trying to achieve full path coverage over time.
All the Features, Scenarios, and Variants are covered by default.
The CLI parameter --files can filter the .feature files to be considered.
The CLI parameter --ignore can indicate the .feature files to be ignored, when a directory is given.
The @ignore can be used to mark a Feature or Variant to be ignored by the test generator. However, it can still be used by other Features or Variants.
The @importance (e.g., @importance( 8 )) can be used to denote the importance of a Feature. CLI parameters --sel-min-feature and --sel-max-feature can then be used to filter the features to be considered by the test generator. Example: concordia --sel-min-feature 7 makes the compiler considers the features with importance value of 7 or above. By default, all the features receive an importance value of 5.
Variants are selected and combined using State-based Strategies (see below).
All the UI Elements constraints are covered by default, using a set of Testing Techniques (see below).
In Concordia, you can declare a State in a Variant sentence using a text between tile (~), like this:
There are three types of State:
Precondition: when declared in a Given sentence;
State Call: when declared in a When sentence;
Postcondition: when declared in a Then sentence.
Both Preconditions and State Calls are considered required States. That is, they denote a dependency of a certain State of the system that needs to be executed. A Precondition needs to be executed before the Variant starts, and a State Call needs to be executed during the Variant's execution.
A Postcondition is a produced State, that is, a state of the system produced by a successful execution of a Variant. Therefore, whether something goes wrong during a Variant's execution, it will not produce the declared State.
When the current Variant requires a State, Concordia Compiler will look for imported Features' Variants able to produce it. To generate complete Test Cases for the current Variant, it will:
Select the Variants to combine;
Generate successful test scenarios for the selected Variants;
Select the successful test scenarios to combine;
Generate (successful and unsuccessful) test scenarios for the current Variant;
Combine the selected successful test scenarios with the test scenarios of the current Variant;
Transform all the test scenarios into test cases (i.e., valued test scenarios).
Steps 1 and 3 can adopt different strategies. Concordia Compiler let's you:
Parameterize how the Variants will be selected, using --comb-variant; and
Parameterize how the successful test scenarios will be combined, using --comb-state.
Available strategies for --comb-variant:
random: Selects a random Variant that produces the required State. That's the default behavior;
first: Selects the first Variant that produces the required State;
fmi: Selects the first most important Variant (since two Variants can have the same importance value) that produces the required State;
all: Selects all the Variants that produce the required State.
Example:
Available strategies for --comb-state:
sre: Single random of each - that is, randomly selects a single, successful test scenario of each selected Variant. That's the default behavior;
sow : Shuffled one-wise - that is, shuffles the successful test scenarios than uses combination.
ow: selection;
all: Selects all the successful test scenarios to combine.
Example:
Strategies that use random selection can take different paths every time they are used. Furthermore, they reduce considerably the amount of generated paths - i.e., it avoids "path explosion" - and thus the amount of produced test cases.
Full-selection strategies can be used for increase path coverage. Although, it also increases the needed time to check all the paths, which may be undesirable for frequent tests.
By default, Concordia Compiler uses random selection strategies.
Concordia Compiler can infer input test data from Variants, UI Elements, Constants, Tables, and Databases. The more constraints you declare, the more test cases it generates.
Adopted techniques to generate input test data include:
These are well-known, effective black-box testing techniques for discovering relevant defects on applications.
We call Data Test Cases those test cases used to generate input test data. They are classified into the following groups: RANGE, LENGTH, FORMAT, SET, REQUIRED, and COMPUTED. The group COMPUTEDis not available on purpose, since a user-defined algorithm to produce test data can have bugs on itself. Thus, one should provide expected input and output values in order to check whether the application is able to correctly compute the output value based on the received input value.
Every group has a set of related data test cases, applied according to the declared constraints and selected algorithms:
By default, the maximum length for randomly-generated string values is 500. This value is used for reducing the time to run test scripts, since long strings take time to be entered.
You can set maximum length using the CLI parameter --random-max-string-size. Example:
You can also set it in the configuration file (.concordiarc) by adding the property randomMaxStringSize. Example:
Data Test Cases (DTC) are selected for every declared and its properties. The more properties you declare for a UI Element, the more data you provide for Concordia Compiler to generate DTC.
Example of some evalutations:
When no properties are declared, FILLED and NOT_FILLED are both applied and considered as valid values;
When the property required is declared, NOT_FILLED (empty) is considered as an invalid value;
When the property value is declared:
if it comes from a set of values (inclusing a query result), all the DTC of the group SET are applied;
otherwise, ...
When the property minimum value is declared:
Let's describe a named Salary :
When no property is defined or only the property data typeis defined,
We defined the property data type as double, since the default data type is string.
Since few restrictions were made, Salary will be tested with the test cases of the group REQUIRED:
FILLED: a pseudo-random double value is generated;
NOT_FILLED: an empty value will be used.
Now let's add a minimum value restriction.
Some tests of the group RANGE are now applicable:
LOWEST_VALUE: the lowest possible double is used
RANDOM_BELOW_MIN_VALUE: a random double before the minimum value is generated
JUST_BELOW_MIN_VALUE: a double just below the minimum value is used (e.g., 999.99)
MIN_VALUE: the minimum value is used
JUST_ABOVE_MIN_VALUE: a double just above the minimum value is used (e.g., 1000.01)
ZERO_VALUE: zero (0) is used
Since 1000.00 is the minimum value, the data produced by the tests 1, 2, 3, and 6 of the group VALUE are considered invalid, while 4 and 5 are not. For these tests considered invalid, the behavior defined in Otherwise, that is
is expected to happen. In other words, this behavior serves as and must occur only when the produced value is invalid.
Unlike this example, when the expected system behavior for invalid values is not specified and a test data is considered invalid, Concordia expects that test should fail. In this case, it generates the Test Case with the tag @fail.
Now let's add maximum value restriction:
All the tests of the group RANGE are now applicable. That is, the following tests will be included:
MEDIAN_VALUE: the median between the minimum and the maximum values
RANDOM_BETWEEN_MIN_MAX_VALUES: a pseudo-random double value between the minimum and the maximum values
JUST_BELOW_MAX_VALUE: the value just below the maximum value
MAX_VALUE: the maximum value
JUST_ABOVE_MAX_VALUE: the value just above the maximum value
RANDOM_ABOVE_MAX_VALUE: a pseudo-random double above the maximum value
GREATEST_VALUE: the greatest possible double
The tests from 5 to 7 will produce values considered invalid.
Let's define a user interface element named Profession and a table named Professions from which the values come from:
Applicable test are:
FILLED
NOT_FILLED
FIRST_ELEMENT
RANDOM_ELEMENT
LAST_ELEMENT
NOT_IN_SET
The first two tests are in the group REQUIRED. Since we declared Profession as having a required value, the test FILLED is considered valid but NOT_FILLED is not. Therefore, it is important to remember declaring required inputs accordingly.
The last four tests are in the group SET. Only the last one, NOT_IN_SET, will produce a value considered invalid.
In this example, let's adjust the past two examples to make Salary rules dynamic and change according to the Profession.
Firstly, we add two columns the the Professions table:
Then, we change the rules to retrieve the values from the table:
The reference to the UI Element {Profession} inside the query, makes the rules of Salary depend on Profession. Every time a Profession is selected, the minimum value and the maximum value of Salary changes according to the columns min_salary and max_salary of the table Professions.
is a browser extension for Chrome and Firefox that converts recordings from (which is a - that is, it records all the actions that you perform while interacting with the application under test in order the reproduce them later) to Variant sentences. That's very handful for discovering elements' identification in web applications (e.g., their id properties or their ) and obtaining Variant sentences with the corresponding interaction.
is a command-line tool that generates User Interface Prototypes (UIP) from Concordia features. It can help discussing features with stakeholders and accelerating their construction. Currently there is a customizable plug-in for HTML-based interfaces.
is a command-line tool that generates an HTML page with an interactive graph that shows features files' relationships.
PINTO, Thiago Delgado. Unifying Agile requirements specification quality control and implementation conformance assurance. 2018. Doctoral Thesis. Pontifical Catholic University of Rio de Janeiro, Informatics Department, 2018.
PINTO, Thiago Delgado; COSTA, Pablo Veiga; GONÇALVES, Willian Inácio. User Interface Prototype Generation from Agile Requirements Specifications written in Concordia. 2019. WebMedia'19: Proceedings on the 25th Brazilian Symposium on Multimedia and the Web, Rio de Janeiro, 2019. ACM. DOI:
Concordia Compiler can use a configuration file in named .concordiarc. You can generate it by running the following command:
You'll be asked about the desired configuration and then the file will be saved.
Example:
Configuration file's properties are similar to CLI parameters. The example above defines: "pt" (meaning "Portuguese") as the default language; the directory "docs/features" for features and test cases; the plug-in "codeceptjs-testcafe" for generating test scripts; the directory "test/e2e" for test scripts; and the directory "test/e2e/output" for output files such as report files and screenshots.
CLI options take precedence over values from the configuration file
directoryDirectory to search for .feature and .testcase files.
Example:
recursiveUse recursive directory search.
Example:
filesFiles to consider, instead of considering directory. The files must be separated by colon.
Example:
Another example:
ignoreFiles to ignore, considering the parameter directory. The files must be separated by colon. Example:
extensionsTo-Do
encodingFile encoding of the files. Accepted values are:
utf8 or utf-8
latin1
ascii
ucs2 or ucs-2
utf16le or utf-16le
Naturally, the files must be edited with the given file encoding.
To-Do
To-Do
To-Do
To-Do
To-Do
Concordia Compiler's versions are based on .
Although Semantic Versioning is conceived for s instead of for applications, we adopt a very similar convention. Thus, changes become predictable and you can know, from the version numbers, when a version is no more compatible with a previous version.
Given a version MAJOR.MINOR.UPDATE:
MAIOR is increased when the Compiler or the Language is no more compatible with the previous version.
MINOR is increased when adding functionality in a backwards-compatible manner.
UPDATE is increased when there are fixes, little changes or little novelties - all of them backwards-compatible.
Examples:
0.2.0 is compatible with 0.1.0
0.1.1 is compatible with 0.1.0
1.0.0 is not compatible with 0.2.0
NPM upgrades without breaking compatibility (when MINORorUPDATE changes).
For migrating a MAJOR version (e.g., 1.x to 2.x) please read our .
To upgrade a local installation:
To upgrade a global installation:
# Simple alert
When I accept the alert
# Alert with a message
When I accept the alert "Do you want to continue?"
# Confirmation dialog
When I accept the confirmation
# Confirmation dialog with a message
When I accept the confirmation "Do you want to continue?"
# Popup dialog
When I accept the popup
# Popup dialog with a message
When I accept the popup "Are you sure?"
# Prompt dialog
When I accept the prompt
# Prompt dialog with a message
When I accept the prompt "What's your name?" # URL
Given that I am on "http://concordialang.org"
# Contant
Given that I am on [Some Page] # Append a value to a UI Element
When I append "Foo" to {Foo}
When I append 100 to {Bar}
When I append [My Contant] to {Zoo}
# Append a value to a UI literal
When I append "Bar" to <#foo>
When I append 200 to <#foo>
When I append [My Contant] to <#zoo> When I attach the file "/path/to/file" to {Foo}
When I attach the file [My File] to {Foo}
When I attach the file "/path/to/file" to <#bar>
When I attach the file [My File] to <#bar> When I cancel the alert
When I cancel the confirmation
When I cancel the popup
When I cancel the prompt When I check {Foo}
When I check <#bar> When I clear {Foo}
When I clear <#bar>
When I clear the cookie "app"
When I clear the cookie [App Cookie] When I click on {Foo}
When I click on <#bar>
When I click on "Some Text"
When I click on [Value From Contant] When I close the current tab
When I close the other tabs
# Mobile only
When I close the app When I connect to the database [TestDB] When I disconnect from the database [TestDB]When I double click {Foo}
When I double click <#bar>
When I double click "Some Text"
When I double click [My Contant] When I drag {Foo} to {Bar}
When I drag <#foo> to <#bar> When I fill {Foo}
When I inform {Foo}
When I enter {Foo}
When I type {Foo}
When I fill {Foo} with "foo"
When I fill {Foo} with [My Contant]
When I fill <#bar> with "bar"
When I fill <#bar> with 3.1415
When I type "bar" in {Foo}
When I type "foo" in <#bar>
When I type 3.1416 in {Zoo}
When I type [My Contant] in <#zoo> # Hides the device's keyboard - Mobile only
When I hide the keyboard When I install the app "com.example.android.myapp" When I maximize the window When I move the cursor to {Foo}
When I move the cursor to <#bar>
When I move the cursor to {Foo} at 100, 200 # Opens the device's notifications panel - Mobile only
When I open the notifications panel When I press "Enter"
When I press "Control", "Alt", "Delete"
When I press "Control", "S" # Mobile only
When I pull "/storage/emulated/0/DCIM/logo.png" to "some/path" When I refresh the page
When I refresh the current page
When I reload the page
When I reload the current page # Mobile only
When I remove the app "com.example.android.myapp" When I resize the window to 600, 400 When I right click {Foo}
When I right click <#bar>
When I right click "Some Text"
When I right click [Contant] # Run a command in the console/terminal
When I run the command 'rmdir foo'
When I run the command './script.sh'
When I run the command [My Command]
# Run a SQL scripts in a database
When I run the script 'INSERT INTO [MyDB].product ( name, price ) VALUES ( "Soda", 1.50 )'
When I run the script 'INSERT INTO [MyDB].Users( UserName, UserSex, UserAge ) VALUES ( "Newton", "Male", 25 )'
When I run the script 'INSERT INTO [MyDB].`my long table name`( `long column`, `another long column`) VALUES ("Foo", 10)'
When I run the script 'UPDATE [MyDB].Users SET UserAge=26, UserStatus="online" WHERE UserName="Newton"'
When I run the script 'UPDATE [MyDB].`my long table name` SET `long column` = "Bar" WHERE `another long column` = 70'
When I run the script 'DELETE FROM [MyDB].Users WHERE UserName="Newton"'
When I run the script 'DELETE FROM [MyDB].`my long table name` WHERE `another long column` = 70' When I run the script 'INSERT INTO [MyDB] VALUES { "name": "Mary", "surname": "Jane", "age": 21 }' When I run the script 'UPDATE [MyDB] SET age = 22 WHERE name = "Mary"' When I save a screenshot to "foo.png"
When I take a photo to "bar.png" When I scroll to <#foo>
When I scroll to {Bar}# Some text
Then I see "Some Text"
Then I see 3.1416
Then I see [Some Contant]
Then I do not see "Some Text"
Then I don't see [Some Contant]
Then see the text "Some Text"
Then I don't see the text "Some Text"
# UI Element or a UI literal
Then I see {Foo}
Then I don't see <#bar>
# Value is/isn't inside a UI Element or a UI literal
Then I see "hello" in {foo}
Then I see "world" in <#bar>
Then I don't see "hello" in {foo}
Then I don't see "world" in <#bar>
Then I do not see {Foo} with "hello"
Then I don't see <bar> with "world"
# Attribute from a UI Element or UI literal
Then I see {Foo} with the attribute "maxlength" valued "200"
Then I see the attribute "type" of <#bar> to be "text"
# CSS class or Style - WEB ONLY
Then I see {Foo} with the class "primary-button"
Then I see {Foo} with the style "color: blue"
# UI Element or ui literal is/isn't enabled
Then I see {Foo} is enabled
Then I see <#bar> is enabled
Then I do not see {Foo} is enabled
Then don't see <#bar> is enabled
# UI Element or ui literal is/isn't checked
Then I see {Foo} is checked
Then I see <#bar> is checked
Then I do not see {Foo} is checked
Then don't see <#bar> is checked
# Title
Then I see "Some Text" in the title
Then I don't see [My Constant] in the title
Then I see the title with "foo"
Then I don't see the title with [My Constant]
# URL
Then I see the url "/foo"
Then I don't see the url "/bar"
# Cookie
Then I see the cookie "foo"
Then I don't see the cookie [My Cookie]
# App is installed/not installed - MOBILE ONLY
Then I see that the app "com.example.android.myapp" is installed
Then I see that the app "com.example.android.myapp" is not installed
# An activity - MOBILE ONLY
Then I see that the current activity is ".HomeScreenActivity"
# Device locked/unlocked - MOBILE ONLY
Then I see that the device is locked
Then I see that the device is locked
# Device orientation - MOBILE ONLY
Then I see that the orientation is landscape
Then I see that the orientation is portrait
When I select "foo" in {Foo}
When I select [Contant] in <#bar> When I shake the device
When I shake the phone
When I shake the tablet # To a certain position
When I swipe <#io.selendroid.myapp:id/LinearLayout1> to 100, 200
# An UI Element/literal to another
When I swipe {Foo} to {Bar}
When I swipe <#foo> to <#bar>
# Down
When I swipe <#io.selendroid.myapp:id/LinearLayout1> down
# Up
When I swipe <#io.selendroid.myapp:id/LinearLayout1> up
# Left
When I swipe <#io.selendroid.myapp:id/LinearLayout1> left
# Right
When I swipe <#io.selendroid.myapp:id/LinearLayout1> right
# IFRAME
# To the first iframe
When I switch to the iframe
# To a certain iframe
When I switch to the iframe {My Frame}
When I switch to the iframe <#frame1>
# To an iframe inside another iframe
When I switch to the iframe {SubFrame 1} inside {Frame 1}
When I switch to the iframe <#subframe1> inside <#frame1>
# Back to the application page
When I switch to the application
When I switch to the page
# TAB
When I switch to the next tab
When I switch to the previous tab
When I switch to the tab 3
# MOBILE-WEB SWITCHING
# To native app
When I switch to native
# To web app
When I switch to web
When I tap {Confirm}
When I tap <~confirm>
When I unckeck {Foo}
When I uncheck <#bar>
# Unchecks an element inside another element
When I uncheck {Foo} in <#bar># -------
# ELEMENT
# -------
# An element
When I wait for {Foo}
When I wait for <#bar>
# An element to hide
When I wait {Foo} to hide
When I wait <#bar> to hide
# An element to be enabled
When I wait {Foo} to be enabled
When I wait <#bar> to be enabled
# An element to be visible
When I wait {Foo} to be visible
When I wait <#bar> to be visible
# An element to be invisible (same as to hide)
When I wait {Foo} to be invisible
When I wait <#bar> to be invisible
# Value inside element
When I wait for the value "foo" in {Foo}
When I wait for the value [My Constant] in <#bar>
# ----
# TEXT
# ----
When I wait for the text "Foo"
# ----
# URL
# ----
When I wait for the url "/foo"
# -------
# SECONDS
# -------
# Seconds
When I wait 1 second
When I wait 2 seconds
# Seconds for an element
When I wait 1 second for {Foo}
When I wait 5 seconds for <#bar>
# Seconds for an element to hide
When I wait 1 second for {Foo} to hide
When I wait 5 seconds for <#bar> to hide
# Seconds for an element to be enabled
When I wait 1 second for {Foo} to be enabled
When I wait 5 seconds for <#bar> to be enabled
# Seconds for an element to be visible
When I wait 1 second for {Foo} to be visible
When I wait 5 seconds for <#bar> to be visible
# Seconds for an element to be invisible (same as to hide)
When I wait 1 second for {Foo} to be invisible
When I wait 5 seconds for <#bar> to be invisible
# Value inside element
When I wait 1 second for the value "foo" in {Foo}
When I wait 5 seconds for the value [My Constant] in <#bar>
# Text
When I wait 3 seconds for the text "Foo"
# URL
When I wait 3 seconds for the url "/foo"
Given that I have ~payment method selected~npx concordia --comb-variant=allnpx concordia --comb-state=allGroup
Data Test Case
Description
RANGE
LOWEST_VALUE
The lowest value for the data type, e.g., lowest integer
RANDOM_BELOW_MIN_VALUE
A random value below the minimum value
JUST_BELOW_MIN_VALUE
The value just below the minimum value, considering the data type and decimal places if applicable
MIN_VALUE
Exactly the minimum value
JUST_ABOVE_MIN_VALUE
The value just above the minimum value, considering the data type and decimal places if applicable
ZERO_VALUE
Zero (0)
MEDIAN_VALUE
The median between the minimum value and the maximum value
RANDOM_BETWEEN_MIN_MAX_VALUES
A random value between the minimum value and the maximum value
JUST_BELOW_MAX_VALUE
The value just below the maximum value, considering the data type and decimal places if applicable
MAX_VALUE
Exactly the maximum value
JUST_ABOVE_MAX_VALUE
The value just above the maximum value, considering the data type and decimal places if applicable
RANDOM_ABOVE_MAX_VALUE
A random value above the maximum value
GREATEST_VALUE
The greatest value for the data type, e.g., greatest integer
LENGTH
LOWEST_LENGTH
An empty string
RANDOM_BELOW_MIN_LENGTH
A string with random characters and random length, less than the minimum length
JUST_BELOW_MIN_LENGTH
A string with random characters and length exactly below the minimum length
MIN_LENGTH
A string with random characters and length exactly equal to the minimum length
JUST_ABOVE_MIN_LENGTH
A string with random characters and length exactly above the minimum length
MEDIAN_LENGTH
A string with random characters and length equal to the median between the minimum length and the maximum length
RANDOM_BETWEEN_MIN_MAX_LENGTHS
A string with random characters and random length, between the minimum length and the maximum length
JUST_BELOW_MAX_LENGTH
A string with random characters and length exactly below the maximum length
MAX_LENGTH
A string with random characters and length exactly equal to the maximum length
JUST_ABOVE_MAX_LENGTH
A string with random characters and length exactly above the maximum length
RANDOM_ABOVE_MAX_LENGTH
A string with random characters and random length, greater than the maximum length
GREATEST_LENGTH
The greatest length supported for a string (see Notes)
FORMAT
VALID_FORMAT
A value that matches the defined regular expression
INVALID_FORMAT
A value that does not match the defined regular expression
SET
FIRST_ELEMENT
The first element in the defined set or query result
RANDOM_ELEMENT
A random element in the defined set or query result
LAST_ELEMENT
The last element in the defined set or query result
NOT_IN_SET
A value that does not belong to the defined set or query result
REQUIRED
FILLED
A random value
NOT_FILLED
Empty value
COMPUTED
RIGHT_COMPUTATION
A value generated by the defined algorithm
WRONG_COMPUTATION
A value different from that generated by the defined algorithm
npx concordia --random-max-string-size=300"randomMaxStringSize": 300UI Element: Salary
- data type is doubleUI Element: Salary
- data type is double
- minimum value is 1000.00
Otherwise I must see "Salary must be greater than or equal to 1000" Otherwise I must see "Salary must be greater than or equal to 1000"UI Element: Salary
- data type is double
- minimum value is 1000.00
Otherwise I must see "Salary must be greater than or equal to 1000"
- maximum value is 30000.00
Otherwise I must see "Salary must be less than or equal to 30000"UI Element: Profession
- type is select
- value comes from "SELECT name from [Professions]"
- required
Table: Professions
| name |
| Lawyer |
| Accountant |
| Dentist |
| Professor |
| Mechanic |Table: professions
| name | min_salary | max_salary |
| Lawyer | 100000 | 900000 |
| Accountant | 90000 | 800000 |
| Dentist | 150000 | 900000 |
| Professor | 80000 | 500000 |
| Mechanic | 50000 | 120000 |UI Element: Salary
- data type is double
- minimum value comes from the query "SELECT min_salary FROM [Professions] WHERE name = {Profession}"
Otherwise I must see "The given Salary is less than the minimum value"
- maximum value comes from the query "SELECT max_salary FROM [Professions] WHERE name = {Profession}"
Otherwise I must see "The given Salary is greater than the maximum value"npm upgrade concordialangnpm upgrade -g concordialangnpx concordia --initType
Default value
CLI option
string
"." (current dir)
--directory
{
"directory": "./features"
}Type
Default value
CLI option
boolean
true
--recursive
{
"recursive": false
}Type
Default value
CLI option
string or string array
(none)
--files
{
"files": "/path/to/file1.feature,/other/file2.feature"
}{
"files": [
"/path/to/file1.feature",
"/other/file2.feature"
]
}Type
Default value
CLI option
string or string array
(none)
--ignore
{
"ignore": [
"/path/to/file1.feature",
"/other/file2.feature"
]
}Type
Default value
CLI option
string
"utf8"
--encoding
Category
Option
Data Type
Default value
Description
Files
directory
string
none
Directory to search for specification and test case files
recursive
boolean
true
Recursive directory search
encoding
string
"utf-8"
Default file encoding
extensions
array of string
[ ".feature" ]
File extensions to search
ignore
array of string
[]
Files to ignore
files
array of string
[]
Files to consider, instead of considering directory
Language
language
string
"en"
Default specification language
Plug-in
plugin
string
none
Plug-in to use
Processing
verbose
boolean
false
Verbose output
compileSpecification
boolean
true
Whether it is desired to compile the specification
generateTestCase
boolean
true
Whether it is desired to generate test case files
generateScript
boolean
true
Whether it is desired to generate test script files
executeScript
boolean
true
Whether it is desired to execute test script files
analyzeResult
boolean
true
Whether it is desired to analyze test script results
dirTestCase
string
same as features'
Output directory for test case files
dirScript
string
"./test"
Output directory for test script files
dirResult
string
"./output"
Output directory of test script results
lineBreaker
string
OS' default
Character(s) used to break lines in text files
Generation
caseUi
string
"camel"
String case used to identify UI Elements when their ids are not defined. Possible values are "camel", "pascal", "snake", "kebab", or "none".
caseMethod
string
"snake"
String case used for test scripts' methods. Possible values are "camel", "pascal", "snake", "kebab", or "none".
tcSuppressHeader
boolean
false
Whether it is desired to suppress header comments in test case files
tcIndenter
string
" "
Character(s) used as indenter for test case files
seed
string
current date and time
Randomic seed used by all the algorithms
randomMinStringSize
integer
0
Minimum size for random strings
randomMaxStringSize
integer
500
Maximum size for random strings
randomTriesToInvalidValue
integer
5
Tries to generate random values that are not in a set of values
Strategies
combVariant
string
"random"
Algorithm to select and to combine the Variants with the state needed by a certain Variant. Options are: "random" to pick a random Variant, "first" to pick the first one, "fmi" to pick the first most important, or "all" to combine them all.
combState
string
"sre"
Algorithm to select and to combine the Test Scenarios of every State of the selected Variants. Options are: "sre" to select a random Test Scenario of every State, "sow" to use the shuffled one-wise algorithm, "ow" to use the one-wise algorithm, or "all" to combine them all.
combInvalid
string
integer
"smart"
Number of UI Elements that will receive invalid data test cases at a time, e.g. 1. String options are "none" for no invalid values, "smart" to let the compiler decide, "random" to select a random number of UI Elements, "all" to select all the invalid values.
combData
string
"sow"
Algorithm to combine data test cases. Options are: "sre" to select a random data test case to combine, "sow" to use the shuffled one-wise algorithm, "ow" to use the one-wise algorithm, or "all" to combine them all.
{
"language": "pt",
"directory": "docs/features",
"plugin": "codeceptjs-testcafe",
"dirScript": "test/e2e",
"dirResult": "test/e2e/output"
}Information about breaking changes.
1.x to 2.xPlease read Issue #56.
0.x to 1.xIt changed the way that all the plug-in operations are handled. See Issue #34 for details.
The behavior of the following plug-in commands were affected:
COMMAND
NOW
BEFORE
plugin-list
It lists all the plug-ins installed for the application.
It listed all the plug-ins available in the compiler.
plugin-install
It installs a plug-in and its dependencies.
Only the plugin's dependencies were installed, since the plug-in was embedded in the compiler.
plugin-uninstall
It uninstalls a plug-in and its dependencies using npm.
Only the plugin's dependencies were uninstalled, since the plug-in was embedded in the compiler.
plugin-info
It shows information about a plug-in installed for the application.
It shows information about a plug-in available in the compiler.
plugin-serve
It starts a testing server using a plug-in installed for the application.
It started a testing server available in the compiler.
👉 See the Command Documentation to know the commands' syntax.
No compatibility breaks.
External tools used by plug-ins are now installed locally, per project, instead of installed globally. Their direct execution (without using Concordia) is now possible through NPX. For instance, the command codeceptjs run test.js must now be executed as npx codeceptjs run test.js. This change also allows the installation of different versions of external tools, when needed.
Were there any changes in commands' syntaxes?
No, there were not.
Is my configuration file still compatible?
It depends. All the properties are still compatible, except for "plugin". Whether your configuration file has that property, you should update it.
Is it possible now to install or uninstall a plug-in with NPM ?
Yes, it is possible to do both now.
Migrations are only needed when upgrading to a major version. See Versions and Upgrade for more information.
To upgrade to a major version:
Uninstall the current version, e.g.: npm uninstall -D concordialang
Install the new version, e.g.: npm install -D concordialang
Now proceed as described below (depending on your current version).
Note: For a global installation, replace -D with -g.
1.x to 2.x1. Update your plug-in
Concordia 2 has a new plugin API, not compatible with plug-ins for Concordia 1. So uninstall your current plug-in and install a new one. Example:
npx concordia --plugin-uninstall codeceptjs-webdriverio
npx concordia --plugin-install codeceptjs-webdriverioAlternatively, you can install one of the new plug-ins then change your configuration file to use it. Example:
npx concordia --plugin-uninstall codeceptjs-webdriverio
npx concordia --plugin-install codeceptjs-playwright
npx concordia --plugin codeceptjs-playwright --save-config2. Install database drivers
Whether your Concordia specification access a database, you have to install the corresponding database driver. Concordia 2 does not install them by default anymore, as Concordia 1 did, aiming to reduce the amount of downloaded dependencies.
Concordia 2 has a new CLI option --db-install to help with that. Example:
npx concordia --db-install mysqlSee Database Drivers to install the proper driver(s) for your application.
0.x to 1.x1. Update your configuration file, if needed
Whether you project has a configuration file .concordiarc, open it with a text editor. If the file has a property "plugin" with the value "codeceptjs", you must change it to "codeceptjs-webdriverio".
2. Install the new plug-in
You can install any of the available plug-ins, currently codeceptjs-webdriverio or codeceptjs-appium. Example:
concordia --plugin-install codeceptjs-webdriverioConcordia Compiler uses plug-ins for generating test scripts, running them and reading their results.
A plug-in can...
be coded in JavaScript or any language that "transpiles" to JavaScript, such as TypeScript (recommended), Dart, or CoffeeScript.
generate code (test scripts) for any programming language or testing framework able to perform functional tests for web, desktop or mobile applications.
Its name must start with concordialang-, for example: concordialang-my-awesome-plugin.
It must be installable with NPM. We recommend you to publish it at NPM too.
It must implement the interface Plugin from concordialang-plugin.
Its package.json file must contain the property concordiaPlugin, like in the following example:
{
...
"concordiaPlugin": {
"file": "dist/index.js",
"class": "MyAwesomePlugin",
"serve": "npx command-to-start-my-testing-server"
}
}Properties of concordiaPlugin:
file: string: Relative path of the file that contains an implementation of the Plugin interface.
class: string: Class (name) that implementsPlugin.
serve: string: Multi-platform console command to execute in order to start the testing server. Whether the testing framework does not need running a testing server, you can set something like echo \"Running a testing server is not needed.\".
A plug-in must deal with three tasks:
Transforming Abstract Test Scripts into test scripts (i.e., source code).
Executing the produced test scripts, regarding some execution options.
Transforming test execution results (produced by the testing framework) to the expected resulting format.
Let's get into the details on how to accomplish them.
Think of an Abstract Test Script (ATS) as an object to transform into source code. Basically, it contains a structure that represents the actions to be converted into commands of your preferred programming language.
For instance, consider the following action:
{
"action": "click",
"targets": ["#ok"]
}That could be converted to the following command in Codeception, for PHP:
I->click("#ok");Or to the following command in Cypress, for JavaScript:
cy.get('#ok').click();Let's consider a more realistic example, starting with a Test Case in the hypothetical file feature1.testcase:
import "feature1.feature"
@generated
@scenario(1)
@variant(1)
Test Case: Scenario 1 | V1
Given that I am on "https://myapp.com/register"
When I fill <#email> with "john.doe@example.com"
and I fill <#nome> with "John Doe"
and I click on <#register>
Then I see "Welcome, John Doe"That would probably produce an Abstract Test Script like this:
{
"feature": { "name": "Feature 1" },
"scenario": { "name": "Scenario 1" },
"testcases": [
{
"scenario": "V1",
"commands": [
{
"action": "amOn",
"values": [ "https://myapp.com/register" ],
"location": { "line": 7, "column": 2 }
},
{
"action": "type",
"targets": [ "#email" ],
"values": [ "john.doe@example.com" ],
"location": { "line": 8, "column": 2 }
},
{
"action": "type",
"targets": [ "#name" ],
"values": [ "John Doe" ],
"location": { "line": 9, "column": 4 }
},
{
"action": "click",
"targets": [ "#register" ],
"location": { "line": 10, "column": 4 }
},
{
"action": "see",
"values": [ "Welcome, John Doe" ],
"location": { "line": 11, "column": 2 }
}
]
}
]
}A plug-in for Cypress could generate the following code from the above object:
context("Feature 1", function() {
it("Scenario 1 | V1", function() {
cy.visit("https://myapp.com/register"); // (7,2)
cy.get("#email").type("john.doe@example.com"); // (8,2)
cy.get("#name").type("John Doe"); // (9,4)
cy.get("#register").click(); // (10,4)
cy.get("body").should("contain", "Welcome, John Doe"); // (11,2)
});
});To help generating such commands, you can use some template library such as Mustache (recommended) or Handlebars.
You can observe that after every command there is a line comment containing the corresponding location in the .testcase file. These comments will help to track the specification in case of test script failures.
To execute the produced test scripts, you can use the target framework API or run the tool. Your call.
For example, whether you chose to run Cypress through npx and generate a report using Mocha JUnit Reporter, the command to run could be the following:
npx cypress run --reporter junitNow the plug-in needs to read the report and transform it into an object of the class TestScriptExecutionResult that will be returned to Concordia Compiler.
For instance, the core of the plug-in for CodeceptJS reads the report from a JSON file generated by using CodeceptJS with Mocha Multi Reporters. The current implementation is available in the class ReportConverter.
A plug-in must be able to track back the location of failures or errors in the corresponding .testcase file from a test script. When a test script has a failure or an error, the report saves the stack trace where it occurred. For instance, tests/feature1.js:20:4 indicates, respectively, the file, line and column where the problem happened. You can use these data to read the line of the test script file and retrieve the line comment that contains the corresponding line in the .testcase file.
You can use a regular expression to extract the data from the stack trace, like ReportConverter did in the method extractScriptLocationFromStackTrace. Then you can use the class FileInstrumentationReader from concordialang-plugin to retrieve the location from the line comment. Example:
const scriptLocation = /* extract from stack trace */;
// Suppose that `failureLocation` is:
// {
// path: "tests/feature1.js",
// line: 7,
// column: 8
// }
// Retrieve the comment with the reader:
const specLocation = reader.retrieveSpecLocation(
scriptLocation
);
// `specLocation` would be:
// {
// path: "features/feature1.testcase",
// line: 10,
// column: 4
// }You can read these locations in the examples described earlier. Whether tests/feature1.js:7:8 is...
cy.get("#register").click(); // (10,4)Retrieving the line 10 and column 4 fromfeature1.testcase would give us:
and I click on <#register>In TestScriptExecutionResult, both test failures and errors are represented as an exception which includes the properties scriptLocation and specLocation.
We recommend you to follow these steps when creating a plug-in:
1. Open an issue to indicate that you are interested in developing a new plug-in for a certain testing framework or programming language. That is good way to make others aware of your work.
2. Choose a good name for your plug-in and check whether it registered at NPM. Remember that it must start with concordialang-. We recommend that it includes the target framework, tools or language. Example: concordialang-selenium-python to create a plug-in that generates test scripts for the Selenium framework and the Python language. You may use the opened issue (step 1) to get feedback about the name, if you wish.
3. Create a repository for it (e.g., at GitHub, GitLab, BitBucket) and clone the repository.
4. Create a NPM package with npm --init.
5. Add the property concordiaPlugin to your package.json file.
6. Install concordialang-plugin as a dependency:
npm install --save concordialang-plugin7. Add your files. We recommend the following structure, but you can adapt it as you wish. For example, if you use TypeScript, you will probably add a tsconfig.json file. If you use Jest, your test folder will probably be named __tests__. We also recommend you to add a .editorconfig file and a .gitignore file (the latter should include the folder node_modules).
┃
┣ src/ 🡐 your source code
┣ test/ 🡐 your tests
┣ .editorconfig
┣ .gitignore
┗ package.json8. Implement the interface Plugin. We recommend you to take a look at other plugins that did a similar job and encourage you to unit test all your code.
9. Test it with the Concordia Compiler before publishing it. Create a simple project that uses your plug-in. You can use NPM to install your plug-in from a directory in your computer or your remote repository. For example:
cd path/to/your/test-project/
npm install /path/to/your-plugin10. Publish your plug-in at NPM after making sure that you are using Semantic Versioning. Congratulations! Tell everybody and ask for feedback! ✌
npm publish


Ideas and proposals
Work in progress and issues:
(GitHub)
(GitHub)
Yes, this project does not have a logo yet 😱. Designers interested in helping us (for free 🙏), please contact us on our or .
The logo should reflect the idea behind the language:
The tool generates test cases and test scripts that verify if the application under test meets the specified functional requirements. Therefore, the idea of "verification" or "testing" could be in the logo.
Interested? See .
👉 Our next goal is a plugin for .
Suggestions:
JavaScript and friends: (web), (web, mobile, desktop)
PHP: (web)
Java: (web), (android), (desktop)
Ruby: (web), (mobile applications developed with )
Python: (web)
C++: (desktop), (desktop)
Delphi: (desktop), (desktop)
Other programming languages or frameworks are welcome! 💖
A Variant Background defines steps that must be executed before all the Variants of a certain Scenario. Example:
MOTIVATION: It allows to keep initial, repeated steps in a common place.
Like this:
MOTIVATION: Simpler syntax.
Please see .
MOTIVATION: It allows to perform visual comparison and detect related bugs.
To use the characters """ (three quotation marks) to denote a text content to be compared. Example:
MOTIVATION: It facilitates comparisons with text files or multiple-line strings.
To allow a given UI Element or UI Literal to match a certain Table.
Example 1:
Example 2:
In which Some Table is declared like this:
It should make target table's rows to match the declared ones.
MOTIVATION: It facilitates the verification of expected data in tables from a user interface.
NOTES: Probably it requires to convert the Concordia Action to many abstract commands that verify every table row independently. Usually testing frameworks do not provide such kind of verification (for many rows at once).
Use Concordia 2 data and time expressions inside table rows. They must be written between ` or some other character. Example:
MOTIVATION: Concordia Language allows date and time expressions inside UI Properties. Using them inside tables can be useful.
States that vary according to some generated value. Example:
According to the selected user, it will produce a different State. For example, when "bob" is selected, the produced state will be admin is logged in and when "joe" is selected, the produced state will be guest is logged in .
Thus, Features could depend on static or dynamic states.
MOTIVATION: Making states more flexible.
To provide a Given sentence that requires one - and only one - of the provided states. Example:
MOTIVATION: It allows to perform a XOR operation with the given states using natural language, aiming at choosing one and only one of the given states.
To provide annotations parameterize a test combination strategy for a specific Feature.
For instance, to configure the combination of invalid values for a specific Feature:
The annotations should correspond to the CLI parameters.
MOTIVATION: Increase the flexibility of the test generation procedure.
Currently:
Proposal (to accept as valid):
Alternative proposal:
MOTIVATION: Make it easier to write/read SQL statements.
Select a Feature for Test Case generation, without having to include its dependencies:
by importance value
Select a Scenario for Test Case generation:
by importance value (tag )
Test Case for test script generation;
by importance value (tag )
Provide the option --watch to generate .testcase files when their corresponding .feature file changes or an imported .feature file changes. A new seed must be produced, except when explicitly provided (using --seed or configuration file).
Concordia has language constructions that the Compiler does not support yet. Examples:
Variant Background: Implement Variant Background, which is part of the Concordia Language but was not implemented yet by the Concordia Compiler.
Support the tag in Features and Scenarios. Currently it is supported inVariants and Test Cases. Whether added to a Feature or a Scenario, it would not generate Test Cases.
Consider global UI Elements
Make the tool processing UI Elements tagged with @global.
Allow inheritance of UI Elements
Use @extends( <name> ) to extend another UI Element.
Example:
Multiple declared Tables per query: Allow a query to reference more than one table declared in the specification.
Multiple declared Databases per query: Allow a query to reference more than one database declared in the specification.
Test cases that explore SQL Injection: Using declared Databases and Queries to formulate Test Cases that generate strings with SQL Injection commands.
Test cases that use naughty strings as test data: Using a as test data.
It is already possible to generate HTML or even PDF reports using the available plug-ins. However, these reports indicate the frameworks' results, not necessarily the ones from Concordia.
To provide the CLI parameter --report for indicating the corresponding plug-in. Example:
which should be equivalent to
Create new projects for auto-completion plug-ins for text editors such as VS Code, Sublime Text, Atom, etc. Example for VS Code: .
Be able to run test scripts using Concordia from the IDE.
Create integration with . Other reporters (e.g., ) can be added further.
Keep some hash control or use Git information when available. Hashes can be stored in a .json or .yml file. Example:
NodeJS has made considerable progress since version 10.5 adding support to . Stable support for Worker Threads was added in version 12 LTS. Although, it would increase minimum requirements to install Concordia Compiler - currently it requires NodeJS 8.0.
This page tries to identify in which version of the Compiler a certain language construction became available.
👁🗨 "Planned" and "Available" refer to versions of the Compiler.
*=Not used yet. **=It may not be released, since it is not needed.
This page presents Concordia actions and their corresponding commands for some frameworks.
Help us improving this page. Send us a message on or open an Issue at . 💖
👉 Under construction.
CLI
Basic syntax is:
where:
A parameter between[ and ] means that it is optional.
[dir] is the directory that contains your .feature files. Whether it is not informed, the tool searches all the subdirectories from the current path.
[options] changes how the tool behaves.
You can run concordia --help to see all the command line interface (CLI) options.
You need to use npx before the commands if you opted for a .
Indicates a
Alias: -c
By default the configuration file is loaded from the current directory.
Creates a and optionally installs plug-ins and databases.
This command asks about your preferences. By hitting Enter without giving an answer, it will select the suggested (default) value. You can use Up and Down to navigate the answers and hit Enter to select it. For multiple selection (when available), hitSpace.
By using a (e.g. .concordiarc) you avoid informing the configured parameters in the console/terminal. For instance, whether plugin is defined in the configuration file, you don't need to inform --plugin anymore.
Add CLI parameters to the configuration file
The following example will consider the given directory and add it to the configuration file:
Indicates the directory that has
.featureand.testcasefiles.
Alias: -d
When not given, it will search the current directory and all subdirectories recursively.
Example:
This is equivalent to:
Specifies
.featurefiles to consider
Aliases: -f, --files
Files must be separated by comma.
Whether the command also informs a directory (e.g. ), it searches the files inside the directory. Example:
The example above will search for ./feature/f1.feature and ./feature/f1.feature.
Specify
.featurefiles to ignore when a directory is informed
Alias: -i
Files must be separated by comma.
Disables recursive search when a directory is informed
Specifies the directory for test reports and screenshots.
Alias: -O
Specifies the directory for test script files.
Alias: -o
Sets the default language to consider.
Alias: -l
The language code must have only 2 characters and needs to be one of the .
Lists the available language codes.
Lists the available locales.
Just generates test scripts
Just verifies specification files
Just generates test cases
Just execute test script files
Avoids generating test scripts
Avoids processing specification files
Avoids generating test cases
Avoids reading test scripts' results
Avoids running test scripts
Uses a verbose output
Number of parallel instances to execute.
Alias: -I
Executes browsers in .
Alias: -H
Sets one or more test script files to execute
Aliases: -F, --script-files
Files must be separated by comma.
Sets a text or regular expression to filter the files to execute
Alias: -G
Sets target browsers or platforms to execute
Alias: -T
Browser or platforms must be separated by comma.
Uses the given plug-in (if installed)
Alias: -p
Shows information about an installed plug-in
Alias: --plugin-info
Installs a plug-in
Lists installed plug-ins
Starts a testing server for a plug-in
Alias: -S
The testing server will keep running. Thus, it's a good idea to open a separate terminal/console for it. To stop it later, type Ctrl + C.
Uninstalls a plug-in
Installs a database driver
Lists installed databases
Uninstalls a database driver
Maximum random string size to generate, when free values are allowed.
The default value is 500 .
Minimum random string size to generate, when free values are allowed.
The default value is 0 .
Random tries to generate invalid values.
When there is a set of valid values and any value different from those in the set is considered an invalid, random generation is used to produce an invalid value. This parameter defines how many tries to take. Usually it gets on the first try.
The default value is 5 .
Sets a
Concordia Compiler can use random selection in its algorithms. By default, it generates the seed using the current date and time. Using the same seed makes the algorithms produce the same output (e.g., same values and paths). While that's interesting for reproducing a past behavior, we don't recommend it for finding new defects. Using new seeds, Concordia Compiler will select different test data, test oracles, and test paths over time.
--comb-data
--comb-invalid
--comb-state
--comb-variant
Sets the character(s) used for breaking lines.
Since by default the line breaker is detected automatically, this command may not be useful for you in most cases.
Note on line breakers:
Linux uses "\n"
MacOS uses "\r"
Windows uses "\r\n"
Sets the expected file encoding
Alias: -e
The default is utf8. Possible values are:
ascii
latin1
ucs2 or ucs-2
utf16le or utf-16le
utf8 or utf-8
Shows information about the compiler.
Shows the available commands
Verify if there is a newer version.
Shows the current version.
Informal specification
It makes the compiler to ignore a piece of text. Example:
They are:
#language: <value> defines the document's language, where <value> is an available language such as en (English) or pt (Portuguese).
#locale: <value> defines document's locale, where <value> is an available local such as en-US (USA English) or pt-BR (Brazilian Portuguese). NOT AVAILABLE YET.
Example:
A tag adds metadata to a declaration.
A tag starts with @. Multiple tags can be declared in a single line. Examples:
Some tags can receive parameters. Examples:
A special tag changes the compiler's behavior. These are special tags:
@scenario( <number> ): references a by its index, starting at 1.
@variant( <number> ): references a by its index, starting at 1.
@importance( <number> ): indicates the importance of a declaration. The importance is as high as its number.
@generated: indicates that a was computer-generated.
@fail: indicates that a should fail.
@ignore: whether applied to a , the Variant will not produce Test Cases; whether applied to a it will not produce test scripts.
@generate-only-valid-values: avoids that a 's property be used for generating values considered invalid. This is specially useful for using with masked input fields, in which the system does not let a user to type invalid characters on it. For instance:
The above example will avoid generating invalid input values (e.g., "A") for testing the format of the Year.
Reserved for future use:
@global makes a UI Element globally accessible. Global UI Elements must not have the same name. Local UI Elements (the default) overrides global UI Elements.
@extends( <name> )
A common tag is ignored by the compiler and aims to provide meaningful metadata for a declaration. Any tag that is not a special tag is considered a common tag. Examples:
@critical may indicate that the current feature is critical for the project;
@issue(20) may indicate that a feature (or scenario or variant) is being implemented in the Issue 20 from the Issue control system.
Desired behavior of a software system
A Feature can be a piece of functionality or some needed, wished or distinguish characteristic of a system.
Example:
Feature sentences should try to describe the contribution that the Feature brings to the business addressed by the software system. They are optional, but recommended. Example:
In the above example the benefit to the business is to prevent unauthorized access.
Concordia adopts a wide-spread template for describing them: . A User Story template goes like this:
The order of the sentences can vary. A <role> is usually a type of user or stakeholder. A <capability> is what the role wants to perform in order to achieve the <benefit>, which often is the contribution to the business.
A certain state of the system
A State is denoted by a text between tilde (~), such as ~a state~, and must be written inside sentences. Example:
A can both produce or require states. A State is produced by a Then sentence, like in the prior example. A State is required by a Given or a When sentence. For instance:
By declaring a required State, you're establishing a dependency among the current and the ones that can produce it. Since that State is often produced by a Variant from another Feature, you have to the corresponding Feature file. Concordia Compiler will only search for states from imported Feature files.
When transforming Variants into , Concordia Compiler will:
Remove any (Then) sentences with a produced State; and
Replace any (Given or When) sentences with a required State by sentences from the Variant that can produce it.
Whether there are different s that can produce a (same) required State, Concordia Compiler will be able to combine each of them with the current Variant, aiming to produce that explore different execution paths. For more information, please read the section .
Let's say that we have a simple Login feature like this:
Now let's suppose that a user has to be logged in to access its account details:
Concordia Compiler will produce my-account.testcase with a like the following, that includes sentences from both the features:
High-level, business-focused, usage scenario of a Feature.
Example:
Template:
Test-oriented declaration that describes a possible interaction between a user and the system in order to accomplish a Scenario.
A Variant must use the Given-When-Then template (see ) and first person singular ("I") in its sentences. Concordia Compiler uses techniques to understand them.
See the in Variant sentences.
Example:
Things that can be part of Variant sentences:
References to , such as [New Account Screen]
References to , such as {Name} or {New User:Name}.
, such as <#ok>
, such ~registered user~
Values such as "bob@example.com"
Numbers such as 123
Make sure that your preconditions are part of the group with Given sentences.
Make sure that your actions are part of the group with When sentences.
Make sure that your post-conditions are part of the group with Then sentences.
Whenever possible, use a single Given , a single When and a single Then sentence. Useand for the other sentences.
Write a single action per sentence.
Do not use andin the middle of a sentence (break it, instead).
Use an additional indentation for and sentences.
Variant sentences can have widgets (of the user interface) denoted between < and >. Example:
Concordia adopts the well-known to locate widgets. Plugins convert these selectors to the format adopted by the testing framework.
Available locators:
Whether you are testing a web application, you may find useful. It's a browser extension - available for Google Chrome and Mozilla Firefox - that extends Katalon Recorder to save a recorded interaction as Variant sentences. Using it may reduce the time needed to locate the widgets of an existing application.
Declaration block with constant values
A Constants block can define one or more constants, preceded by a dash sign (-). Both constant names and values are declared between quotes, except for numeric values - that can be declared without quotes. Example:
Constant values are accessible by their names between [ and ]. Examples for the constants above:
Defines a data table that can be used by UI Elements' properties.
A table must have a unique name. Their rows are denoted between pipe (| ). The first row must provide the column names. Other rows must provide the corresponding data. Example:
Table declarations are transformed into in-memory tables that can be queried by UI Element properties for selecting test data. Tables can be queried using SQL. Like , their names must be referenced between [ and ]. Example:
Declares a database connection that can be used for test data selection
Example 1:
Example 2:
Database support needs installation of the corresponding drives. See for more information on how to install them.
Properties:
Imports declarations from a
.featurefile
Examples:
Construction
Planned
Available
#
0.1
0.x
#language
0.1
0.x
#locale
2.1
Not released yet.
@*
0.1
0.x
@scenario
0.1
0.x
@variant
0.1
0.x
@ignore
0.1
0.x
@importance
0.1
0.x*
@generated
0.1
0.x
@fail
0.1
0.x
@global
0.1
Not released yet.
@extends
0.1
Not released yet.
@category
0.1
Not released yet.**
@issue
0.1
Not released yet.**
@generate-only-valid-values
1.0
1.1
Feature
0.1
0.x
Scenario
0.1
0.x
Background
0.1
Not released yet.**
Variant
0.1
0.x
Variant Background
0.1
Not released yet.
~state~
0.1
0.x
Constants
0.1
0.x
Table
0.1
0.x
Database
0.1
0.x
UI Element
0.1
0.x
UI Element property locale
1.4
2.0
UI Element property locale format
1.4
2.0
UI Element properties with date and time expressions
1.4
2.0
Test Case
0.1
0.x
Before All
0.1
0.x
After All
0.1
0.x
Before Feature
0.1
0.x
After Feature
0.1
0.x
Before Each Scenario
0.1
0.x
After Each Scenario
0.1
0.x
Feature: Search
Scenario: Simple Search
Variant Background:
Given that I visit the [Search Page]
When I fill {Search} with "Hello"
Variant: Search by clicking on Go
When I click on {Go}
Then I see "Hello World!"
Variant: Search by hitting Enter
When I press "Enter"
Then I see "Hello World!" Constants:
- Net Price is 10.0
- hello is "hello" ...
Then I see the file "out.txt" with
"""
This is some text to be compared.
It will probably
be compared to a
text file.
"""Then I see the table {MyTable} as [Some Table]Then I see the table <myTable> as [Some Table]Table: Some Table
| Name | Age |
| Bob | 21 |
| Suzan | 25 |Table: Some Table
| Name | Age |
| Bob | `18 years ago` |...
Given that I fill {User}
and I fill {Pass}
and I click on {OK}
Then I see "Welcome"
and I have ~{User Type} is logged in~
UI Element: User
- value comes from "SELECT user FROM [Users]"
UI Element: Pass
- value comes from "SELECT pass FROM [Users] WHERE user = {User}"
UI Element: User Type
- value comes from "SELECT user_type FROM [Users] WHERE user = {User}"
Table: Users
| user | pass | user_type |
| bob | 123456 | admin |
| joe | 654321 | guest |
| alice | 123456 | admin |Variant: Example 1
Given that I have either ~x~ or ~y~
...
Variant: Example 2
Given one of ~x~, ~y~
...@comb-invalid(1)
Feature: Example- value comes from "SELECT name FROM [MyDB].profession"- value comes from "SELECT name
FROM [MyDB].profession"`- value comes from
"""
SELECT name
FROM [MyDB].profession
"""UI Element: Name
- min length is 2
- max length is 100
@extends( Name )
UI Element: Emergency Contact Name
# Emergency Contact Name inherits the properties from Namenpx concordia --report htmlnpx concordia --report concordialang-report-html{
"hashes": {
"feature1.testcase": "ox10JBprHtu5c8822XooloNKUfk=",
"subdir/feature2.testcase": "DMcj5b67Albe4KhpzyvphC5nVDHn1oCO",
}
}@importance@importance@ignoreConcordia
Action [+target]
Appium
CodeceptJS
Cypress
Playwright
Puppeteer
TestCafé
amOn
amOnPage
goto
check
checkOption
check
clear + element
clearField
N/A
clear + cookie
clearCookie
clearCookies
click
click
doubleClick
doubleClick
dblclick
fill
fill
N/A
focus
move
moveTo
moveCursorTo
trigger
hover
hover
hover
press
press
refresh
refreshPage
reload
resize
resizeWindow
setViewportSize
rightClick
rightClick
saveScreenshot
saveScreenshot
screenshot
scrollTo
scrollTo
scrollTo
select
selectOption
selectOption
switch + element
switchTo
N/A
switch + next tab
switchToNextTab
N/A
switch + previous tab
switchToPreviousTab
N/A
tap
tap
N/A
type
type
wait + seconds
wait
N/A
wait + element
waitForElement
waitForElement
uncheck
uncheckOption
uncheck
concordia [dir] [options]concordia --config /path/to/.concordiarcconcordia --initconcordia --save-config --directory ./featuresconcordia --directory ./featuresconcordia ./featuresconcordia --file "file1.feature,/path/to/file2.feature"concordia ./feature --file "f1.feature,f2.feature"concordia ./feature --ignore "f3.feature,f4.feature"concordia ./features --no-recursiveconcordia --dir-result outputconcordia --dir-script testconcordia --language ptconcordia --language-listconcordia --locale-listconcordia --just-scriptconcordia --just-specconcordia --just-scriptconcordia --just-runconcordia --no-scriptconcordia --no-specconcordia --no-test-caseconcordia --no-resultconcordia --no-runconcordia -xconcordia --verboseconcordia --instances 2concordia --headlessconcordia --script-file "test1.js,test2.js"concordia --script-grep "Sales"concordia --target "chrome,firefox,edge"concordia --plugin <plugin>concordia -p <plugin> -xconcordia --plugin-info <plugin>concordia --plugin-install <plugin>concordia --plugin-listconcordia --plugin-serve <plugin>concordia --plugin-uninstall <plugin>concordia --db-install <database>concordia --db-listconcordia --db-uninstall <database>concordia --random-max-string-size 100concordia --random-min-string-size 1concordia --random-tries 10concordia --seed="example"concordia --line-breaker "\n"concordia --encoding latin1concordia --aboutconcordia --helpconcordia --newerconcordia --version# This is a comment
Feature: Pay with Credit Card # This is also a comment#language: pt
Funcionalidade: Pagar com Cartão de Crédito...
@critical @slow
Feature: Print Coupon
...
@gui
Variant: Print to PDF
...
@device
Variant: Print to Plotter
@importance( 6 )
Feature: Register Employee
...
@extends( Date )
UI Element: Birth Date
... UI Element: Year
- data type is integer
@generate-only-valid-values
- format is "/^[0-9]{1-3}$/"
Otherwise I must see "Year must be a number."Feature: Administrator LoginFeature: Login
As a registered user
I would like to access to the system using my credentials
So that the system prevents unauthorized accessAs a <role>
I would like to <capability>
So that <benefit>Then I have a ~user logged in~Given that I have ~user logged in~Feature: Login
Scenario: Sucessful login
Variant: Login with username and password
Given that I am on the [Login Screen]
When I enter with "bob" in {Username}
and I enter with "123456" in {Password}
and I click on {OK}
Then I see "Welcome"
and I have ~user logged in~
Constants:
- "Login Screen" is "/login"
UI Element: Username
UI Element: Password
UI Element: OK
- type is buttonimport "login.feature"
Feature: My Account
Scenario: See my account data
Variant: Show basic data by default
Given that I have ~user logged in~
and I am on the [Account Screen]
Then I see {User} with "bob"
and I see {Name} with "Robert Downey Jr"
Constants:
- "Account Screen" is "/account"
UI Element: Userimport "my-account.feature"
@scenario(1)
@variant(1)
Test Case: See my account data - 1
Given that I am on the "/login"
When I enter with "bob" in <username>
and I enter with "123456" in <password>
and I click on <ok>
Then I see "Welcome"
Given that I am on the "/account
Then I see <user> with "bob"
and I see <name> with "Robert Downey Jr"Feature: Sales Report
Scenario: Daily sales report
Given that I am authenticated as a manager
When I open the sales report
and I choose the option "Daily report"
Then I see a list with all the today's sales
and the total valueGiven <some context or precondition>
and <other context or precondition>
and <other context or precondition>
...
When <some action is carried out>
and <other action is carried out>
and <other action is carried out>
...
Then <a result or postcondition is observed>
and <another result or postcondition is observed>
and <another result or postcondition is observed>
...Feature: New User
Scenario: Successful registration
Variant: Register with a new account
Given that I am on the [New Account Screen]
When I fill {Name}
and I fill {Date of Birth}
and I enter with "bob@example.com" in <#email>
and I inform 123 in <#pass>
and I type 123 in <#confirmation>
and I click <#ok>
Then I have a ~registered user~
and I see "Welcome, Bob"
...When I fill <#firstName> with "Alice"Locator
Purpose
Example
#
To find a widget by its id.
When I click on <#save>
.
To find a widget by its class.
When I click on <.btn-primary>
@
To find a widget by its name.
When I click on <@save>
~
To find a widget by its mobile name.
When I click on <~save>
//
To find a widget by its XPath.
When I click on <//form/div/button[1]>
(none)
To find a widget by its type.
When I click on <button>
Constants:
- "Home" is "/home"
- "App Name" is "Acme"
- "Mininum Age" is 18
- "Pi" is 3.1416Variant: Example
Given that I visit [Home]
and I see [App Name]
# ...
UI Element: Age
- minimum value is [Minimum Age]
Otherwise I see "Invalid age."
UI Element: Some Math Value
- maximum value is [Pi]Table: professions
| name | min_salary |
| Magician | 100000.00 |
| UI Element: Profession
- value comes from "SELECT name FROM [professions]"
UI Element: Salary
- minimum value comes from "SELECT min_salary from [professions] WHERE name = {Profession}"Database: My DB
- type is "mysql"
- host is "http://127.0.0.1"
- name is "mydb"
- username is "admin"
- password is "p4sss"Database: Another DB
- type is "json"
- path is "C:\path\to\db.json"type
Database type. Examples: "adodb", "csv", "firebase", "ini", "json", "mysql", "mssql", "postgres", "sqlite", "xlsx". See Using Databases for all supported types.
Yes
host
URL that indicates where the database is hosted.
Vary
port
Network communication port used to connect to the database. Whether not defined, the default database port will be used, according to the property type.
No
name
Database name. Used when there is a database server (whose location is defined by the property host) and the database is accessible by its name.
Vary
path
Database path. Used when the database is accessed through the file system, such as the types csv, ini, json, sqlite and xlsx.
Vary
username
Username used to connect to the database. When not defined, the database's default user will be used, according to the property type.
No
password
Password used to connect to the database. When not defined, the database's default password will be used, according to the property type.
No
options
Database-specific connection options.
No
import "file1.feature"
import "path/to/file2.feature"Variant