FitNesse: workflow of creating tests using RestFixture

sample picture sample · Mar 2, 2016 · Viewed 8.6k times · Source

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

Answer

smartrics picture smartrics · Mar 30, 2016

This answer provides a cookbook to install RestFixture and a section to present the Acceptance test framework.

Installation

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.

Assumptions

  • you have a valid and working JDK installed (try from command prompt java -version)
  • you have maven installed (try mvn --version)
  • you have a version control software installed. Although, I won't discuss how to use it in the context of this answer.

Cookbook

  1. 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

    • Change the names of the group and artifact accordingly, to suite you
    • Remove the files src/test/java/smartrics/restfixture/sample/*.java src/main/java/smartrics/restfixture/sample/*.java
  2. cd scratch (or to whatever name you have chosen) and edit pom.xml

  3. Add the RestFixture dependency (the latest version of RestFixture is here). For example:
<dependency>
        <groupId>smartrics.restfixture</groupId>
        <artifactId>smartrics-RestFixture</artifactId>
        <version>4.1</version>
</dependency>
  1. Add the Maven dependencies plugin to copy dependencies locally (this is not necessary but I find it convenient)
<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>
  1. Right now, you should be able to mvn package and build/copy dependencies (check them in the target/dependencies directory)
  2. You may want to update to the latest FitNesse version (Check the latest version available here), by adding
<dependency>
    <groupId>org.fitnesse</groupId>
    <artifactId>fitnesse</artifactId>
    <version>20151230</version>
</dependency>
  1. Then do mvn clean package
  2. Create a file 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 
  1. start FitNesse with the start.bat (it will take some time for FitNesse to create all the relevant resources)
    • From your browser go to http://localhost:9090 to check it works
    • You can stop FitNesse with CTRL-C
  2. You should at this point consider to commit to version control. If you use git, these items may be added to the ignore list:
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
  1. With FitNesse running, got to the home page and edit.

    • Add this wiki word MyTestSamples somewhere in the page, then save.
    • You should see MyTestSamples[?] because the page doesn't exist.
    • Click on the question mark to edit the page
    • Append the following lines

    !define TEST_SYSTEM {slim} !path target/dependencies/*.jar

    to add a reference to the RestFixture classes and dependencies and use the Slim Test system

    • Save the page.
  2. In the MyTestSamples page Add a new Test page from the Add menu.
    • call the page MySample1, save
  3. Go to MySample1, 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|
  1. If all went OK, by hitting Test on the page, the test should run and pass.

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.

Logging

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

  • The pom.xml is here
  • The start.bat is here

Acceptance tests

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:

  1. Via Java: extend the RestFixture java to add/change the behaviour. For examples check the (RestFixtureExtensions)12 project.
  2. Via Javascript: As described below

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;| |