- A Kurtosis user account
- A Docker engine with version >= 2.3.0.0
- Visit the list of supported language clients
- Pick your favorite, and clone the repo to your local machine
- Run the
bootstrap/bootstrap.sh
script from inside the cloned repo - Follow the language-specific instructions in the new
README.md
file
From the root directory of your bootstrapped repo:
git init
to initialize your new repogit add .
to stage all the files you've changedgit commit -m "Init commit"
to commit the files (don't skip this - it's needed to run the testsuite!)- Run
scripts/build_and_run.sh all
to run the testsuite
If everything worked, you should see an output that looks something like this:
INFO[2020-10-15T18:43:27Z] ==================================================================================================
INFO[2020-10-15T18:43:27Z] TEST RESULTS
INFO[2020-10-15T18:43:27Z] ==================================================================================================
INFO[2020-10-15T18:43:27Z] - singleNodeExampleTest: PASSED
INFO[2020-10-15T18:43:27Z] - fixedSizeExampleTest: PASSED
(This output is from the Kurtosis Go client; other languages might be slightly different)
If you don't see success messages, check out the guide for debugging failed tests which contains solutions to common issues. If this still doesn't resolve your issue, you can ask for help in the Kurtosis Discord server.
Now that you have a running testsuite, you'll want to start customizing the testsuite to your needs. Here we'll walk through the components inside a testsuite, as well as how to customize them.
NOTE: This tutorial will avoid language-specific idioms and use a Java-like pseudocode notation in this documentation, but will reference the example in the Go implementation to illustrate. All object names and methods will be more or less the same in your language of choice, and all language repos come with an example.
You're writing a Kurtosis testsuite because you want to write tests for a network, and networks are composed of services. To a test, a service is just an API that represents an interaction with the actual Docker container. To give your tests this API, define an implementation of the Service
interface that provides the functionality you want for your test like this. Here you'll provide the functions you want to be able to call on the service, as well as two Kurtosis-required bits:
Service.getIpAddress
, a getter to retrieve the service's IP and a check if the service is available. Your service should take in the IP address as a constructor parameter, and return it with this function; later we'll see how this gets passed to the constructor.Service.isAvailable
, a check you'll need to implement to tell Kurtosis when your service should be considered available and ready for use.
By now, you should have an implementation of the Service
interface that represents an instance of your service.
Our tests now have a nice interface for interacting with a service running in a Docker container, but we need to tell Kurtosis how to actually start the Docker container running the service. This is done by implementing the DockerContainerInitializer
interface. This interface will be very well-documented in code in your language, so you can use the documents there to write an initializer like this. Of note: this is where you'll pass in the IP address to the constructor of your Service
implementation.
Now that we have a service, we can use it in a test. Each test is simply an implementation of the Test
interface, and each has a Test.setup
method which performs the work necessary to setup the testnet to a state where the test can run over it. You should use the NetworkContext.addService
method to create instances of your service like this.
The addService
call return an instance of your service, as well as an AvailabilityChecker
object. The waitForStartup
method of the checker is a polling wrapper around Service.isAvailable
that you wrote earlier, and can be used to block until the service instance is up or a timeout is hit.
Finally, the Test.setup
method must return a Network
object. This returned object will be the same one passed in as an argument to the Test.run
method, which the test can use to interact with the network. For now, you can return the NetworkContext
object.
Every implementation of the Test
interface must fill out the Test.run
method. This function takes in the Network
object that was returned by Test.setup
, and uses the methods on the TestContext
object to make assertions about the state of the network like so. If no failures are called using the TestContext
, the test is assumed to pass.
It's not very useful to test just one service at a time; we're using Kurtosis because we want to test whole networks. This means that we need services which depend on other services. Fortunately, this is easily done by passing the dependency Service
interface in the dependent's DockerContainerInitializer
constructor like you would any other object, like this API service which depends on the datastore service.
Then, when instantiating the network in Test.setup
, simply instantiate the dependency first and the dependent second, like this.
Now that you have a test, your last step is to package it into a testsuite. A testsuite is simply an implementation of the TestSuite
interface that yields a set of named tests, like this. This is also where you'll thread through parameterization, like what Docker image the tests should run with.
With your testsuite complete, your only remaining step is to make sure it's getting used. When you bootstrapped your testsuite repo, you will have received an entrypoint main function that receives several flags, instantiates a testsuite, and passes that to the Kurtosis client like this Go example. You will also have received a Dockerfile, for packaging that main CLI into a Docker image (Go example). What build_and_run.sh
actually does during its "build" phase is compile the main entrypoint CLI and package it into an executable Docker image. In order for your testsuite to get run, you just need to make sure the main entrypoint CLI is using your testsuite.
You now have a custom testsuite running using Kurtosis!
So far your Test.setup
method has returned the Kurtosis-provided NetworkContext
, and your Test.run
method has consumed it. This can be enough for basic tests, but you'll often want to centralize the network setup logic into a custom object that all your tests will use. Kurtosis allows this by letting your Test.setup
method return any implementation of the Network
marker interface; the Test.run
will then receive that same Network
object as an argument. To see this in action, the Go example testsuite has this custom Network
object, which makes the Test.setup
of complex networks a whole lot simpler by encapsulating all the DockerContainerInitializer
instantiation and waiting-for-availability.
You'll notice that one of the flags that the example entrypoint CLI above receives is serviceImageArg
, which defines the name of the Docker image to use in the services in the network. This a custom parameter - a parameter not used by Kurtosis itself but by the testsuite. Your testsuite might also need additional parameters. To pipe these through, you'll need to:
- Add another flag to the main CLI and pass it to your
TestSuite
object's constructor - Modify the Dockerfile to set the flag value using an environment variable
These environment variables in the Dockerfile might seem like magic, but they're just set by Kurtosis when it launches the Docker image containing your testsuite. You'll need to tell Kurtosis what values to use for your custom flags, which can be done at time of launch with the CUSTOM_ENV_VARS_JSON
parameter to the Kurtosis initializer. For example, if your Dockerfile uses the MY_CUSTOM_VAR
environment variable then you might call build_and_run.sh
with --env CUSTOM_ENV_VARS_JSON="{\"MY_CUSTOM_ENV_VAR\":5}"
.
Now that you have your own custom testsuite running, you can:
- Take a look at the testsuite for the Avalanche (AVAX) token, a real-world Kurtosis use-case
- Visit the Kurtosis testsuite docs to learn more about what's in a testsuite and how to customize it to your needs
- Check out the CI documentation to learn how to run Kurtosis in your CI environment
- Take a step back and visit the Kurtosis architecture docs to get the big picture
- Pop into the Kurtosis Discord to join the community!