Testing
Dependencies¶
To use Csw Testkit, you must add the following dependency in your project:
libraryDependencies += "com.github.tmtsoftware.csw" %% "csw-testkit" % "0.7.0"
Introduction¶
CSW comes with a dedicated csw-testkit
module for supporting tests. This module includes following multiple individual testkits:
LocationTestKit
: starts and stops location serverConfigTestKit
: starts and stops config serverEventTestKit
: starts and stops event service (Note : This usesembedded-redis
to start redis sentinel and master)AlarmTestKit
: starts and stops alarm service (Note : This usesembedded-redis
to start redis sentinel and master)FrameworkTestKit
: in most of the cases, you will end up using this testkit.FrameworkTestKit
is created by composing all the above mentioned testkits. Hence it supports starting and stopping all provided csw services.
All the testkits requires location server to be up and running. Hence first thing all testkits does is to start location server. You do not need to start it explicitly.
TestKits¶
When you really want a granular level access to testkits then only you would want to use LocationTestKit
|ConfigTestKit
|EventTestKit
|AlarmTestKit
|FrameworkTestKit
directly. You can create instance of FrameworkTestKit
as shown below:
// create instance of framework testkit
private val frameworkTestKit = FrameworkTestKit()
// starts Config Server and Event Service
override protected def beforeAll(): Unit = frameworkTestKit.start(ConfigServer, EventServer)
// stops all services started by this testkit
override protected def afterAll(): Unit = frameworkTestKit.shutdown()
Similarly you can use other testkits. Please refer API docs for more details.
Spawning components¶
FrameworkTestKit
provides easy way to spawn components in Container
or Standalone
mode. Use spawnContainer
method provided by FrameworkTestKit
to start components in container mode andspawnStandalone
method to start component in standalone mode.
Below example show how to spawn container or component in standalone mode using framework testkit.
// starting container from container config using testkit
frameworkTestKit.spawnContainer(ConfigFactory.load("SampleContainer.conf"))
// starting standalone component from config using testkit
// val componentRef: ActorRef[ComponentMessage] =
// frameworkTestKit.spawnStandaloneComponent(ConfigFactory.load("SampleHcdStandalone.conf"))
// starting container from container config using testkit
frameworkTestKit.spawnContainer(ConfigFactory.load("JSampleContainer.conf"));
// starting standalone component from config using testkit
// ActorRef<ComponentMessage> componentRef =
// frameworkTestKit.spawnStandaloneComponent(ConfigFactory.load("SampleHcdStandalone.conf"));
Full source at GitHub
Test framework integration¶
ScalaTest¶
If you are using ScalaTest then you can extend csw.testkit.scaladsl.ScalaTestFrameworkTestKit
to have framework test kit automatically start provided services before running tests and shutdown it when the test is complete. This is done in beforeAll
and afterAll
from the BeforeAndAfterAll
trait. If you override that method you should call super.beforeAll
to start services and super.afterAll
to shutdown the test kit.
JUnit¶
If you are using JUnit then you can use csw.testkit.javadsl.FrameworkTestKitJunitResource
to have the framework test kit automatically start provided services before running tests and shutdown it when the test is complete.
Supported CSW Services by FrameworkTestKit¶
ScalaTestFrameworkTestKit
and FrameworkTestKitJunitResource
both support starting one or more of the following services.
CSWService.LocationServer
|JCSWService.LocationServer
CSWService.ConfigServer
|JCSWService.ConfigServer
CSWService.EventServer
|JCSWService.EventServer
CSWService.AlarmServer
|JCSWService.AlarmServer
Below example show’s the usage of ScalaTestFrameworkTestKit
and FrameworkTestKitJunitResource
and how you can start above mentioned services as per your need.
import com.typesafe.config.ConfigFactory
import csw.testkit.scaladsl.CSWService.{AlarmServer, EventServer}
import csw.testkit.scaladsl.ScalaTestFrameworkTestKit
import org.scalatest.FunSuiteLike
class ScalaTestIntegrationExampleTest extends ScalaTestFrameworkTestKit(AlarmServer, EventServer) with FunSuiteLike {
test("test spawning component in standalone mode") {
spawnStandalone(ConfigFactory.load("SampleHcdStandalone.conf"))
// .. assertions etc.
}
}
import com.typesafe.config.ConfigFactory;
import csw.testkit.javadsl.FrameworkTestKitJunitResource;
import csw.testkit.javadsl.JCSWService;
import org.junit.ClassRule;
import org.junit.Test;
import org.scalatestplus.junit.JUnitSuite;
import java.util.Arrays;
public class JUnitIntegrationExampleTest extends JUnitSuite {
@ClassRule
public static final FrameworkTestKitJunitResource testKit =
new FrameworkTestKitJunitResource(Arrays.asList(JCSWService.AlarmServer, JCSWService.EventServer));
@Test
public void testSpawningComponentInStandaloneMode() {
testKit.spawnStandalone(ConfigFactory.load("JSampleHcdStandalone.conf"));
// ... assertions etc.
}
}
You do not need to externally start any services like event, config, location etc. via csw-services.sh
script. Testkits will start required services as a part of initialization. For event and alarm service, it uses embedded-redis
.
Unit Tests¶
The goal of unit testing is to break your application into the smallest testable units, and test them individually, isolating a specific piece of functionality and ensuring it is working correctly. It is always a good idea to write more unit test cases and relatively fewer component and integration tests. If you want to get an idea of how many tests you should have in different types of testing phases (Unit/Component/Integration), refer this blog
Unit testing simple scala/java classes or objects is straight forward. You can mock external dependencies using Mockito. Refer to the Mockito section for more details.
The following links provide guides for testing applications using different modules of Akka:
Multi-JVM Tests¶
Testing asynchronous distributed systems requires special tooling/framework support. Sbt has a plugin called sbt-multi-jvm which helps to test systems across multiple JVMs or machines. This is especially useful for integration testing where multiple systems communicate with each other.
You can find more details on multi-JVM tests here.
You can also refer csw for writing your own multi-JVM tests. For example: CommandServiceTest.scala
In case you want to run your multi-JVM tests across machines, refer this multi-node testing guide here.
Mockito¶
Mocks are used so that unit tests can be written independent of dependencies.
csw uses Mockito for writing unit tests. ScalaTest comes with MockitoSugar trait which provides some basic syntax sugar for Mockito.
For example: ContainerBehaviorTest.scala
Acceptance Tests¶
This section explains how and where csw maintains and executes acceptance tests. If you are a component writer and want to maintain acceptance tests, you can create a repo similar to csw-acceptance and update dependencies, projects as per your need.
As required by TMT Systems Engineering, the acceptance pipeline runs all the existing Java and Scala tests from csw repo on published bintray binaries rather than directly on source code.
More information can be found here.
Below are the two separate Jenkins pipelines to run csw
acceptance tests:
-
- Automatically triggered every night to get fast feedback and intended for developer’s visibility.
-
- Automatically triggered on completion of csw-prod pipeline.
csw-prod-release
pipeline published CSW artifacts to bintray, and must be manually triggered by an administrator.
csw-prod pipeline is responsible for following tasks:
- build and run
csw
tests - publish binaries to bintray
- publish paradox documentation
- publish apps and release notes to github releases
- trigger acceptance-release pipeline
Acceptance pipelines can also be triggered manually via an HTTP end point, for STIL acceptance tesing, for example. Using the security token obtained from the Jenkins pipeline settings (available upon request), run the curl
cmd as shown below:
- For triggering acceptance-dev pipeline, run below
curl -G 'http://ec2-35-154-215-191.ap-south-1.compute.amazonaws.com:8080/job/acceptance-dev/buildWithParameters' \
--data-urlencode token=$DEV_TOKEN \
--data-urlencode CSW_VERSION=0.1-SNAPSHOT
- For triggering
acceptance-release
pipeline, run below: (Modify parameters as applicable)
curl -G '$REMOTE_JENKINS_URL/job/$JOB_NAME/buildWithParameters' \
--data-urlencode token=$RELEASE_TOKEN \
--data-urlencode CSW_VERSION=$CSW_VERSION