Newbie to FitNesse, I want to test Rest APIs using RestFixture but did not know where to start.
Can someone give me step by step workflow and the sample decision table for GET, POST, and DELETE requests?
We use JSON responses, how can I input them in the decision tables when posting it?
Thanks
This answer provides a cookbook to install RestFixture and a section to present the Acceptance test framework.
One way to understand how to start with RestFixture is look at RestFixtureLiveDoc. The project provides the documentation for RestFixture itself and it is managed by Maven. Should you prefer going this way, please read below.
java -version
)mvn --version
)Create an empty Maven project (as explained here). In a nutshell:
mvn archetype:generate -DgroupId=smartrics.restfixture.sample -DartifactId=scratch -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
src/test/java/smartrics/restfixture/sample/*.java
src/main/java/smartrics/restfixture/sample/*.java
cd scratch
(or to whatever name you have chosen) and edit pom.xml
<dependency>
<groupId>smartrics.restfixture</groupId>
<artifactId>smartrics-RestFixture</artifactId>
<version>4.1</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<configuration>
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
</configuration>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
mvn package
and build/copy dependencies (check them in the target/dependencies
directory)<dependency>
<groupId>org.fitnesse</groupId>
<artifactId>fitnesse</artifactId>
<version>20151230</version>
</dependency>
mvn clean package
start.bat
with this content, to create a start command for fitnesse (On Linux you should be able to create an equivalent start.sh
):setLocal EnableDelayedExpansion
set CLASSPATH="
for /R target\dependencies %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!"
java -cp !CLASSPATH! fitnesseMain.FitNesseMain -p 9090 -d src\main\resources -e 0
start.bat
(it will take some time for FitNesse to create all the relevant resources)
http://localhost:9090
to check it worksCTRL-C
target/
src/main/resources/FitNesseRoot/files/testResults
src/main/resources/FitNesseRoot/ErrorLogs
src/main/resources/FitNesseRoot/RecentChanges
src/main/resources/FitNesseRoot/properties
src/main/resources/FitNesseRoot/updateDoNotCopyOverList
src/main/resources/FitNesseRoot/updateList
src/main/resources/FitNesseRoot/Fitnesse/UserGuide
src/main/resources/FitNesseRoot/PageFooter
src/main/resources/FitNesseRoot/PageHeader
src/main/resources/FitNesseRoot/TemplateLibrary
With FitNesse running, got to the home page and edit.
MyTestSamples
somewhere in the page, then save.MyTestSamples[?]
because the page doesn't exist.!define TEST_SYSTEM {slim}
!path target/dependencies/*.jar
to add a reference to the RestFixture classes and dependencies and use the Slim Test system
MyTestSamples
page Add a new Test page from the Add menu.
MySample1
, saveMySample1
, edit the page and type the following content. Then save| Import |
| smartrics.rest.fitnesse.fixture |
|Table: Rest Fixture | http://jsonplaceholder.typicode.com/ |
| GET |/users | 200 | Content-Type : application/json; charset=utf-8 | jsonbody.length == 10|
You could achieve the same setup by constructing the project by hand by downloading and unzipping the RestFixture with dependencies (download from the link bin.zip here). But I find the Maven approach simpler in the longer run.
RestFixture uses slf4j but doesn't come with logger bridge. To complete the configuration of the logging system add the following dependencies:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.20</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
Then create the configuration file at src\main\resources\log4j.xml
with the following content
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="file" value="restfixture.log" />
<param name="append" value="false" />
<param name="threshold" value="debug" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] - %m%n" />
</layout>
</appender>
<category name="smartrics.rest.client" additivity="false">
<priority value="INFO" />
<appender-ref ref="FILE" />
</category>
<category name="smartrics.rest.fitnesse.fixture" additivity="false">
<priority value="DEBUG" />
<appender-ref ref="FILE" />
</category>
<category name="httpclient.wire" additivity="false">
<priority value="ERROR" />
<appender-ref ref="FILE" />
</category>
<root>
<priority value="ERROR" />
<appender-ref ref="FILE" />
</root>
</log4j:configuration>
The httpclient.wire
logger can be set to DEBUG to observe HTTP traffic on the wire.
For logging in the FitNesse server (outside the scope of this tutorial) check here
JUnit runner:
FitNesse tests can be run via JUnit as well, using the Maven Surefire plugin.
Create a test class runner in src/test/java/smartrics/restfixture/sample/RunnerTest.java
package smartrics.restfixture.sample;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import fitnesse.junit.JUnitHelper;
public class RunnerTest {
private JUnitHelper helper;
@Before
public void prepare() {
helper = new JUnitHelper("src/main/resources", new File("target", "fitnesse-junit").getAbsolutePath());
helper.setPort(9090);
}
@Test
public void restFixtureTests() throws Exception {
helper.assertSuitePasses("MyTestSamples.MySample1");
}
}
Then install the surefire plugin in the pom file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<forkMode>always</forkMode>
</configuration>
</plugin>
Save the pom.xml file and run mvn clean package
to run the build with tests.
The build will pass and the report will be available in /target/fitnesse-junit/MyTestSamples.MySample1.html
References
RestFixture has been designed to focus on the REST API exposed by the service under test. Mixing and matching Wiki content with structured fixtures does provide the best of both worlds for providing live documentation.
RestFixture can also be extended to provide additional functionality.
When writing acceptance tests focused on the API it's important highlight the parts of an HTTP request/response interaction: the VERB, the URI, the request/response BODY, the request/response HEADERs and the RETURN status code.
RestFixture does so by expressing each of these parts in a FitNesse table form:
| Table: Rest Fixture | <base uri> |
| setBody | <body> |
| setHeaders | <headers>
| VERB | /URI | <expected return status code> | <expected response headers | <expected response body> |
The <expected ...>
cells may be empty. If empty, no expectation will be checked.
setHeaders
and setBody
are only relevant for verbs requiring an entity (POST, PUT, ...)
Moreover, by using let
it's possible to extract data from parts of the HTTP response and share it with other fixtures and tests.
(RestFixtureLiveDoc)1 has a comprehensive set of examples for the various features implemented by RestFixture, but here follows - for reference - an extract of the main ones.
RestFixtureConfig
(RestFixtureConfig)11 is a simple fixture to set RestFixture configuration parameters. Config parameters can be overridden in named config fixtures and passed on to RestFixture instances. For example
|Table: Rest Fixture Config | my_config |
| name1 | value1 |
| name2 | value2 |
|Table: Rest Fixture | http://host:8080/path | my_config |
| ... |
RestFixture operations
The supported HTTP operations are: GET
, POST
, PUT
, DELETE
, HEAD
, OPTION
, TRACE
. For example:
| Table: Rest Fixture |http://host:8080 |
|GET | /resources/0 | 200 | Content-Type: application/json | jsonbody.name === 'Ted' |
OR
| Table: Rest Fixture |http://host:8080/path |
|GET | /resources/0 | 200 | Content-Type: application/xml | //name[text()='Ted'] |
Depending on the information that the author may want to convey expectation cells (3rd, 4th and 5th cell on each row) may be supplied for verification.
Simple fixtures like the examples above may be provided to test and document the service exposed REST api.
Sharing data
Data is shared using let
to extract from the response.
| Table: Rest Fixture |http://host:8080/path |
|GET | /resources/0 | 200 | Content-Type: application/json | jsonbody.name === 'Ted' |
| let | name | js | response.jsonbody.name | |
In here, the symbol name
is set to the value of the json expression response.jsonbody.name
. response
is a json object containing the last HTTP response, jsonbody
is an object in response containing the JSON object in the response body.
The symbol can be further used in other fixtures:
| setBody | { 'some_name' : '%name%' } |
or
| script | another fixture |
| check | not null | $name |
Transactions
The system can be also test and documented for multiple operations providing an application transaction (in a lose meaning here).
| Table: Rest Fixture | http://host:port |
| comment | an order is created ... |
| POST | /orders | | | |
| let | orderIdUri | header | Location: (.+) | |
| GET | %orderIdUri | | | |
| let | email | js | response.jsonbody.address.email |
| setBody |!- {
'payload' : {
'message' : 'order sent',
'email' : '%email%
}
-!|
| comment | and a confirmation can be sent |
| POST | /notifications | 201 | | |
The example above documents a way to use the API as an application transaction.
Extending RestFixture
RestFixture can be extended to provide additional functionality. Two methods are provided:
Simple Javascript code can be embedded in RestFixture via configuration.
| Table: Rest Fixture Config | imp |
| restfixture.javascript.imports.map | !- sampleJs=src/main/resources/FitNesseRoot/files/javascript/sample.js -|
The config above - when used - will load the Javascript in the file provided as path and make it available in the context of the expectations handler:
| Table: Rest Fixture |http://host:port | imp |
| GET | /resources/1.json | 200 | | |
| let | sum | js | var sum = sampleAdd(1, 2); sum;| |