Kotlin test reporting


Installation

For Kotlin use the Tesults Java API. The Tesults Java API Library is available from the JCenter and Maven Central repositories or as a JAR you can download from here.

Gradle

If you are using Gradle add this dependency snippet to your build.gradle file:

dependencies {
  compile 'com.tesults:tesults:1.0.1'
}

Also ensure you have the JCenter or Maven Central repository referenced in your build.gradle file.

Maven

If you are using Maven add this dependency snippet to your pom.xml file:

<dependency>
  <groupId>com.tesults</groupId>
  <artifactId>tesults</artifactId>
  <version>1.0.1</version>
</dependency>

JAR

Alternatively a JAR is available for download directly here: tesults.jar (compatible with Java 7+)

Configuration

Use one of the three methods described above to make the Tesults Java API available as a library and then import the com.tesults.tesults package in your code:

import com.tesults.tesults.Results;

Usage

Upload results using the upload method in the Results class:

// Results upload - the single point of contact between your code and Tesults
val response = Results.upload(data)

// Response output:
println("success: " + response["success"])
println("message: " + response["message"])
println("warnings: " + (response["warnings"] as List<String>).size);
println("errors: " + (response["errors"] as List<String>).size);

The upload method returns a response that you can use to check whether the upload was a success.

Value for key "success" is a Boolean: true if results successfully uploaded, false otherwise.

Value for key "message" is a String: if success is false, check message to see why upload failed.

Value for key "warnings" is a List<String>, if size is not zero there may be issues with file uploads.

Value for key "errors" is a List<String>, if "success" is true then this will be empty.

The data param passed to upload is a Hashmap<String, Any> containing your results data. Here is a complete example showing how to populate data with your build and test results and then complete upload to Tesults with a call to Results.upload(data):

Complete example:
// Required imports:
import java.util.ArrayList
import java.util.HashMap
import java.util.List
import com.tesults.tesults.Results  // Tesults Java API available as library for using Results class

fun main(args: Array<String>) {
  // In main function as an example only, ordinarily this code would be
  // located wherever your results data is available.

  // A list to hold your test cases.
  val testCases = new ArrayList<Map<String, Any>>();

  // Each test case is of HashMap<String,Any> type. We use generic types rather than
  // concrete helper classes so that if and when the Tesults services adds more fields
  // you do not have to update the library.

  // You would usually add test cases in a loop taking the results from the data objects
  // in your build/test scripts.
  val testCase1 = HashMap<String, Any>()
  testCase1["name"] = "Test 1"
  testCase1["desc"] = "Test 1 description"
  testCase1["suite"] = "Suite A"
  testCase1["result"] = "pass"

  testCases.add(testCase1)

  val testCase2 = HashMap<String, Any>()
  testCase2["name"] = "Test 2"
  testCase2["desc"] = "Test 2 description"
  testCase2["suite"] = "Suite B"
  testCase2["result"] = "pass"

  // (Optional) For a paramaterized test case:
  val params = HashMap<String, String>();
  params["param1"] = "value1"
  params["param2"] = "value2"
  testCase2["params"] = params

  // (Optional) Custom fields start with an underscore:
  testCase2["_CustomField"] = "Custom field value"

  testCases.add(testCase2)

  val testCase3 = HashMap<String, Any>();
  testCase3["name"] = "Test 3"
  testCase3["desc"] = "Test 3 description"
  testCase3["suite"] = "Suite A"
  testCase3["result"] = "fail"
  testCase3["reason"] = "Assert fail in line 203 of example.java."

  // (Optional) Add start and end time for test:
  // In this example, start is offset to 60 seconds earlier
  // but it should be set to current time when the test starts
  testCase3["start"] = System.currentTimeMillis() - 60000;
  testCase3["end"] = System.currentTimeMillis();

  // (Optional) For uploading files:
  val files = ArrayList<String>()
  files.add("/Path/to/file/log.txt")
  files.add("/Path/to/file/screencapture.png")
  files.add("/Path/to/file/metrics.xls")
  testCase3["files"] = files

  // (Optional) For adding test steps:
  val steps = new ArrayList<Map<String, Any>>();

  val step1 = HashMap<String, Any>();
  step1["name"] = "Step 1"
  step1["desc"] = "Step 1 description"
  step1["result"] = "pass"
  steps.add(step1)

  val step2 = HashMap<String, Any>();
  step2["name"] = "Step 2"
  step2["desc"] = "Step 2 description"
  step2["result"] = "fail"
  step2["reason"] = "Assert fail in line 203 of example.java."
  steps.add(step2)

  testCase3["steps"] = steps

  testCases.add(testCase3)

  // HashMap<String, Any> to hold your test results data.
  Map<String, Object> data = new HashMap<String, Any>()
  data["target"] = "token"

  val results = HashMap<String, Any>()
  results["cases"] = testCases
  data["results"] = results

  // Upload
  val response = Results.upload(data)
  println("success: " + response["success"])
  println("message: " + response["message"])
  println("warnings: " + (response["warnings"] as List<String>).size)
  println("errors: " + (response["errors"] as List<String>).size)
}

The target value, 'token' above should be replaced with your Tesults target token. If you have lost your token you can regenerate one from the config menu.

Each test case added to cases must have a name and a result. The result value must be one of 'pass', 'fail' or 'unknown'. All other fields are optional: desc, suite, files, params, steps, reason and custom fields (prefixed with underscore).

Test case properties

This is a complete list of test case properties for reporting results. The required fields must have values otherwise upload will fail with an error message about missing fields.

PropertyRequiredDescription
name*Name of the test.
result*Result of the test. Must be one of: pass, fail, unknown. Set to 'pass' for a test that passed, 'fail' for a failure.
suiteSuite the test belongs to. This is a way to group tests.
descDescription of the test
reasonReason for the test failure. Leave this empty or do not include it if the test passed
paramsParameters of the test if it is a parameterized test.
filesFiles that belong to the test case, such as logs, screenshots, metrics and performance data.
stepsA list of test steps that constitute the actions of a test case.
startStart time of the test case in milliseconds from Unix epoch.
endEnd time of the test case in milliseconds from Unix epoch.
durationDuration of the test case running time in milliseconds. There is no need to provide this if start and end are provided, it will be calculated automatically by Tesults." : "Duration of the build time in milliseconds. There is no need to provide this if start and end are provided, it will be calculated automatically by Tesults.
rawResultReport a result to use with the result interpretation feature. This can give you finer control over how to report result status values beyond the three Tesults core result values of pass, fail and unknown.
_customReport any number of custom fields. To report custom fields add a field name starting with an underscore ( _ ) followed by the field name.

Build properties

To report build information simply add another case added to the cases array with suite set to [build]. This is a complete list of build properties for reporting results. The required fields must have values otherwise upload will fail with an error message about missing fields.

PropertyRequiredDescription
name*Name of the build, revision, version, or change list.
result*Result of the build. Must be one of: pass, fail, unknown. Use 'pass' for build success and 'fail' for build failure.
suite*Must be set to value '[build]', otherwise will be registered as a test case instead.
descDescription of the build or changes.
reasonReason for the build failure. Leave this empty or do not include it if the build succeeded.
paramsBuild parameters or inputs if there are any.
filesBuild files and artifacts such as logs.
startStart time of the build in milliseconds from Unix epoch.
endEnd time of the build in milliseconds from Unix epoch.
durationDuration of the build time in milliseconds. There is no need to provide this if start and end are provided, it will be calculated automatically by Tesults.
_customReport any number of custom fields. To report custom fields add a field name starting with an underscore ( _ ) followed by the field name.

Files generated by tests

The example above demonstrates how to upload files for each test case. In practice you would generate the array of file paths for each test case programatically.

To make this process simpler we suggest you write a helper function to extract files for each test case and this can be easily achieved by following a couple of simple conventions when testing.

1. Store all files in a temporary directory as your tests run. After Tesults upload is complete you can delete the temporary directory or overwrite it on the next test run.

2. Within this temporary directory create subdirectories for each test case so that files for each test case are easily mapped to a particular test case.

  • expanded temporary folder
    • expanded Test Suite A
      • expanded Test 1
        • test.log
        • screenshot.png
      • expanded Test 2
        • test.log
        • screenshot.png
    • expanded Test Suite B
      • expanded Test 3
        • metrics.csv
        • error.log
      • expanded Test 4
        • test.log

Then all your helper function needs to do is take the test name and/or suite as parameters and return an array of files for that particular test case.

import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.util.ArrayList
import java.util.HashMap

internal val tempDir = "/files-temp-dir"

fun filesForTest(suite: String, name: String): List<String> {
  val filePaths = ArrayList<String>()

  val path = Paths.get(tempDir, suite, name) as Path
  val fullPath = path.toString()

  val dir = File(fullPath)

  for (file in dir.listFiles()!!) {
    if (file.name != ".DS_Store") { // Exclude os files
      filePaths.add(file.path)
    }
  }

  return filePaths
}

//testCase.put("files", filesForTest(testCase["suite"] as String, testCase["name"] as String))

Caution: If uploading files the time taken to upload is entirely dependent on your network speed. Typical office upload speeds of 100 - 1000 Mbps should allow upload of even hundreds of files quite quickly, just a few seconds, but if you have slower access it may take hours. We recommend uploading a reasonable number of files for each test case. The upload method blocks at the end of a test run while uploading test results and files. When starting out test without files first to ensure everything is setup correctly.

Consolidating parallel test runs

If you execute multiple test runs in parallel or serially for the same build or release and results are submitted to Tesults within each run, separately, you will find that multiple test runs are generated on Tesults. This is because the default behavior on Tesults is to treat each results submission as a separate test run. This behavior can be changed from the configuration menu. Click 'Results Consolidation By Build' from the Configure Project menu to enable and disable consolidation by target. Enabling consolidation will mean that multiple test runs submitted with the same build name will be consolidated into a single test run.

Dynamically created test cases

If you dynamically create test cases, such as test cases with variable values, we recommend that the test suite and test case names themselves be static. Provide the variable data information in the test case description or other custom fields but try to keep the test suite and test name static. If you change your test suite or test name on every test run you will not benefit from a range of features Tesults has to offer including test case failure assignment and historical results analysis. You need not make your tests any less dynamic, variable values can still be reported within test case details.

Proxy servers

Does your corporate/office network run behind a proxy server? Contact us and we will supply you with a custom API Library for this case. Without this results will fail to upload to Tesults.

Have questions or need help? Contact us