This documentation provides some useful reference material related to using Lightest. For a more hands-on reference, try the Lightest tutorial!
Lightest is a task-oriented functional and integration test automation framework built on Groovy and TestNG. It's a "framework framework" that is intended to greatly simplify the process of creating your own custom framework by implementing the testcase management, execution, and reporting for you - all you do is define the test environment and write the tasks!
You might want to try Lightest if:
The Lightest project is hosted on Google Code. You may download releases, browse the source, or file issues there.
The Lightest framework comes with a runner packaged in an executable JAR. The standalone distribution includes all required dependencies; no external libraries are needed. Tests are run from the command line by specifying a configuration file (see below for syntax) and either a list of Groovy test files (which subclass LightestTestCase, also see below) or a TestNG suite XML file which specifies the test classes and groups to run. The most important configuration to get right is the classPaths element; most other configurations have sensible defaults.
$ java -jar lightest-core-0.4-standalone.jar config.txt MyTest.groovy MyTest2.groovy
$ java -jar lightest-core-0.4-standalone.jar config.txt testng.xml
You can start the runner in interactive mode by providing the right switch:
$ java -jar lightest-core-0.4-standalone.jar --interactive config.txt MyTest.groovy
Lightest requires at least Java 1.5 . Surprisingly, you shouldn't need to install Groovy on your machine to run Lightest. However, you'll probably want to have a reasonably recent version (1.5.4 or later) to use when developing your tasks. If you don't have it, get it here!
The runner produces an HTML report, which in turn is generated from an XML report. HTML is probably the most convenient reporting format; however, it is possible to customize Lightest to create reports in other formats. See the tutorial for screenshots of the reports that are produced.
The configuration file specified as the first argument to the runner follows a Groovy builder syntax, with the config element as its root node.
| Element | Description | Example |
|---|---|---|
| classPaths | Contains You will probably get an error if any class specified in a configuration cannot be loaded from the class paths set here. |
config {
...
classPaths {
path ('/path/to/tasks')
path ('../path/to/tests')
}
...
} |
| outputDir | Specifies the report output directory. The report files will be placed directly in this directory. If unspecified, by default the report will be created in the lightest-report directory in the directory where the test runner was invoked. If more than one directory is specified, only the first entry will be used. | config {
...
outputDir ('/path/to/report')
...
} |
| prefs | Specifies name-value pairs for preferences to be shared across all tests being configured in this run. The preferences must have a corresponding class, which has publicly-accessible properties to match the preference names. There should be only one prefs element. |
config {
...
prefs (class: 'my.package.Preferences') {
timeout (5000)
corpus ('main')
}
...
} |
| envs | Enumerates test environments that are available to the tests being configured in this run. Tests may be run concurrently across these environments. All environments correspond to a single class, but have name-value pairs which may differentiate them. Testcases and tasks are able to access these values for the purpose of interacting with their respective environment. These name-value pairs must match properties of the environment class. Each environment must have a unique There should be only one |
config {
...
envs (class: 'my.package.Environment') {
env (id: 'windows') {
homeDir ('C:\\Documents and Settings\\me')
appName ('my.app')
}
env (id: 'linux') {
homeDir ('/home/me')
appName ('my.app')
}
env (id: 'macos') {
homeDir ('/Users/me')
appName ('my.app')
}
}
...
} |
| reporters | You may specify reporter classes that will be used instead of the Classes that implement only Additionally, you may override the following built-in XML-based reporters - |
config {
...
reporters {
reporter (class: 'my.package.Reporter') {
updateEnabled (false)
scheduled (false)
}
reporter (class: 'my.package.Reporter2')
reporter (class: 'my.package.Reporter3', role: 'XMLReporter') {
prop3 ('value3')
prop4 ('value4')
}
}
...
} |
| listeners | Listeners can be registered with the TestNG engine directly. One or more custom listeners can be specified by class, which must implement org.testng.ITestNGListener; typically either ITestListener or ISuiteListener. Additional properties of each listener may be specified here. |
config {
...
listeners {
listener (class: 'my.package.Listener') {
prop1 ('value1')
prop2 ('value2')
}
listener (class: 'my.package.Listener2')
}
...
} |
| dispatcherAssignmentStrategy | You may specify a strategy class that will be used instead of the default SimpleDispatcherAssignmentStrategy to decide how task dispatchers (which are associated with test environments) get assigned to testcase classes. This class must implement IDispatcherAssignmentStrategy, and may for convenience extend QueuedDispatcherAssignmentStrategy to handle concurrency. There should be only one dispatcherAssignmentStrategy element. |
config {
...
dispatcherAssignmentStrategy (class: 'my.package.Strategy')
...
} |
| taskDispatchStrategy | Sets the dispatch strategy to use instead of the default. The strategy must implement Bean properties can be specified on the strategy in this configuration. |
config {
...
taskDispatchStrategy (class: 'my.package.Strategy') {
prop1 ('value1')
prop2 ('value2')
}
...
} |
The SimpleApi class (com.googlecode.lightest.core.SimpleApi) provides a convenient way to define the set of tasks that are available to a given testcase. It can search one or more Java package paths for tasks by name, and returns the first task found. By default, all tasks in the current working directory of the test runner are included by SimpleApi instances.
Use addPackage(String packageName) to add a new package path to the API. For example, if your tasks are defined in the package com.sample.tasks, you could do:
def api = new SimpleApi()
api.addPackage('com.sample.tasks')
Or simply:
def api = new SimpleApi('com.sample.tasks')
SimpleApi implements IDomainSpecificApi, specifically its getTask(String name) method. You may use a custom implementation of this interface that doesn't obtain tasks by package at all, and uses some other mechanism.
All Lightest tests extend LightestTestCase (com.googlecode.lightest.core.LightestTestCase), which is responsible for properly interpreting tasks specified with the builder syntax in its test methods. Subclasses should call setApi(IDomainSpecificApi api) before invoking any tasks. This can be done in the constructor of the test class, or in a @Before method. In its simplest form:
class MyTest extends LightestTestCase {
MyTest() {
setApi(new SimpleApi())
}
}
LightestTestCase's are run by the TestNG runner; you must always use the @Test annotation to mark test methods and @Before / @After to mark fixture setup and teardown methods, respectively. To make use of JUnit assertions, add the appropriate static import to the top of the file:
import static org.testng.AssertJUnit.*
You can access the test environment (implementing ITestEnvironment) assigned to the testcase in any given test method directly via the env property. You can access the preferences via prefs. You may also obtain either from the context, e.g. context.env:
@Test
void myTest() {
def port = env.port // equivalent to "context.env.port"
...
}
You can add properties to the context in the scope of any test method as if it were a Map, and these can be accessed by any tasks that are invoked, also via the context. Each test method always starts with a fresh context. Note that some special property names are reserved, i.e. env and prefs .
Each task used in a testcase should have as its implementation a corresponding subclass of LightestTask (com.googlecode.lightest.core.LightestTask). The task is responsible for performing some action, and populating a result object (see the next section) depending on whether the action succeeded or failed. Subclasses of LightestTask only have to implement a single method, doPerform(ITaskResult result):
class MyTask extends LightestTask {
void doPerform(ITaskResult result) {
println 'Been there, done that.'
result.fail()
}
}
Three key properties are available for inspection in task classes - config, prefs, and env .
The config object is essentially a groovy.util.Node from which the parameters to the task may be accessed. Attributes are accessed using the .'@attributeName' notation, and values can be accessed with .nodeValue() . For example:
@Test
void myTest() {
MyTask (attr1: 'foo', attr2: 'bar, 'baz')
}
MyTask.groovy
void doPerform(ITaskResult result) {
println config.'@attr1' // prints "foo"
println config.'@attr2' // prints "bar"
println config.nodeValue() // prints "baz"
}
Two attributes on config have special meaning in the context of Lightest - description and breakpoint. If set, the String representation of description will be automatically visible with the task in the report. If for a given task breakpoint is set to any value that evaluates to true, the test will pause at that task if the runner is in interactive mode. You don't have to do anything in the task definition for these features to work.
Any preference values specified in the configuration file may be accessed from prefs. And any environment values may be accessed from env. Also available is the context object.
Executing a task always produces a ITaskResult (com.googlecode.lightest.core.ITaskResult). The values populated on this object will appear in the report for the test run. The important values to consider setting are status, message, detailedMessage, and links . The ITaskResult object is passed into the doPerform() method, and should be populated therein.
The status of the result is what determines whether the task succeeded or failed. By default, the status is set to indicate success (STATUS_OK). Setting it to anything else indicates failure at some level. In order of increasing severity, you can invoke flag(), fail(), or doom() on the result object to do this.
Use setMessage(String message) to set an informational message to be displayed in the report. In particular, display any information related to why the task may have failed.
Use setDetailedMessage(String detailedMessage) to set larger chunks of text that may have been produced in performing the task.
Finally, use addLink(String link) to add a link to related resources, such as documents or screenshots. The link text will appear in the report, exactly as specified using this method. Multiple links may be specified. Use addLink(TaskResultLink link) if you need greater control of the HTML rendering of the link.
The following table describes the lifecycle of a Lightest task, which is an implementation of ITask (and typically, but not necessarily, a subclass of LightestTask). This may be useful to know if you want to inject custom logic at specific points of the lifecycle.
| # | Step Name | Step Description |
|---|---|---|
| 1 | Build TaskNode |
A task is "invoked" in the test when a call for a missing method is encountered in the test. The test class' (i.e. LightestTestCase's) methodMissing() logic assumes the missing method call conforms to Groovy builder syntax, and builds a TaskNode representing the task its child tasks, if any. |
| 2 | Create Task Instance | The test class' api object, which implements IDomainSpecificApi, is queried for an instance of the task. If a SimpleApi is being used, a new task is instantiated simply by invoking the task class' no-argument constructor. |
| 3 | Call configure() |
The task instance's Auto-wiring takes effect for class MyTask extends LightestTask {
boolean waitForSignal
...
}
and the task was invoked as: MyTask (waitForSignal: 'false') the boolean (not waitForSignal == config.'@waitForSignal' The two exceptional cases are for the attributes config.'@name' config.'@value' |
| 4 | Dispatch Task | dispatch() is invoked on the ITaskDispatchStrategy associated with the test, passing the configured task instance. In many cases (including the default case, where the SimpleTaskDispatchStrategy is in play), the strategy simply invokes the task's perform() method. Subclasses of LightestTask enjoy protection from exceptions being thrown from the task; their task logic is implemented within doPerform(). |
| 5 | Perform Child Tasks | If the task has any child tasks, and if the task succeeded, child tasks are now performed, starting at creating them via the api (they should already exist in the TaskNode tree). If the task failed, all child tasks are skipped. |
- Haw-Bin Chai (hbchai @t gmail d0t com)