-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This is the rrt Wiki. Here you can find some more information and examples of how to use this library. For further question join the Gitter channel.
To write a refactoring test with this library is quite easy. First you define which data you need to generate your requests:
import com.github.pheymann.rrt._
val myTest = for {
userNames <- genStaticData("Luke", "Anakin")
userAges <- genPositiveInts(100)
result <- ...
Here we say we need some user names and provide a static set and an age generator. And as a user age of 0 or 200 doesn't makes much sense we say it should be a non-zero integer below 100.
Next we describe how to create requests from this data:
result <- testGet { _ =>
val uri = s"/my/service/user/${userNames()}"
val params = Params().add("age", userAges)
uri |+| params
}
} yield result
We say we want to test a GET endpoint with the URI /my/service/user/:userName?:age
. Now the last step is
to execute our test. Therefore we have to provide a configuration:
val config = TestConfig("my test", ServiceConfig("newservice.com", 80), ServiceConfig("oldservice.com", 80))
.withRepetitions(10)
Finally we run the test case:
assert(checkAndLog(myTest.runSeq(config)))
Here we executed the test, logged possible errors and checked the result.
You can use rrt as stand-alone library in a script or wrap it into some automated test framework like scalatest or specs.
- test configuration
- random value generators
- random database selectors
- sequence and options of random values
- parameter builder
- request builder: uri only, with parameters, with body
- own-body-comparison
The test configuration is done in-code by using the TestConfig
class.
Usage:
val config = TestConfig("test-name", ServiceConfig("url-new-service.com", 80), ServiceConfig("url-old-service.com", 80)
The above fields are mandatory. Furthermore it makes sense to put the ServiceConfig
in some trait
or object to reuse it for multiple tests.
The basic configuration can be extended with:
-
withRepetitions
: How often the test is repeated. -
withHeaders
: Adds standard headers to every request. -
withIgnoreByRegex
:List
of regexes which will replace elements in the response body with an emptyString
. -
withIgnoreJsonKeys
:List
of Json keys which will be removed from the response jsons together with values. -
withThrottling
: Defines a maximum request rate. -
withTimeout
: Sets a timeout value for the requests. -
withDatabase
: Configures a single database connection. -
showJsonDiff
: Generates and shows the JSON differences of the response bodies when set to true.
You can generate random values by using the following generators.
Usage:
for {
userAges <- genPositiveInts(100)
}
-
genStaticData[A](values: A*)
: You provide a list of static values and the generator is selecting elements randomly from it. -
genInts(max: Option[Int])
: A generator forInt
values with an optional maximum bound. -
genPositiveInts(max: Option[Int])
: A generator for positiveInt
values (0 <) with an optional maximum bound. -
genLongs(max: Option[Int])
: A generator forLong
values with an optional maximum bound. -
genDoubles(max: Option[Int])
: A generator forDouble
values with an optional maximum bound.
You can also provide data from some database if randomly generated values aren't sufficient, e.g. ids.
Usage:
for {
userNames <- retrieveStrings(100).from("user_table", "id", "name")
}
Furthermore you have to provide a configuration for the database connection:
val config = TestConfig(...).withDatabase(MySQL, "com.mysql.jdbc.Driver", "jdbc:mysql://localhost", "user", "password")
Or if you use Play
you can just include you database conf:
import com.github.pheymann.rrt.util.play._
val config = TestConfig(...).withDatabase(MySQL, "my-database")
-
retrieveInts(maxSize: Int)
: Randomly selects at mostmaxSize
Int
elements. -
retrieveLongs(maxSize: Int)
: Randomly selects at mostmaxSize
Long
elements. -
retrieveDouble(maxSize: Int)
: Randomly selects at mostmaxSize
Double
elements. -
retrieveStrings(maxSize: Int)
: Randomly selects at mostmaxSize
String
elements.
You can also generate a sequence or Option
of random values:
Usage:
for {
userNames <- ...
results <- testGet { implicit rand =>
val names = userNames.toSeq(10)
val nameOpt = userNames.toOpt
...
}
} yield result
You have to provide the RandomUtil
instance as implicit function parameter.
There is some syntactic sugar for building the parameters Map[String, String]
called Params
.
Usage:
val params = Params().add("age", ageGenerator).add("name", nameGenerator)
You can also provide a Map
and add it to the constructor of the class. Thus, you are able the
reuse common parameters.
Usage:
result <- testPost { _ =>
...
uri
}
result <- testPost { _ =>
...
uri |+| params
}
Usage:
result <- testPost { _ =>
...
uri |+| params |+| jsonString |=| ContentTypes.`application/json`
}
Or without parameters:
result <- testPost { _ =>
...
uri |+| jsonString |=| ContentTypes.`application/json`
}
The String
comparison function can be replaced by providing an own implementation.
Usage:
com.github.pheymann.rrt.util._
val myComparison: BodyComparison = (actual, expected, config) => {
//do my comparison here
...
// you can also use `FailedWithDiff(element, diff)`
if (failed)
Some(FailedWithValues("body", actual, expected))
else
None
}
val myTest = for {...}
assert(checkAndLog(myTest.runSeq(config, comparison = myComparison)))