This guide walks you through adding marklogic-unit-test to an existing project, followed by writing, loading, and running a simple test.

Example project description

The example project for this guide is a simple MarkLogic application with a custom REST endpoint named “thesaurus”. The endpoint returns thesaurus entries via the /example/lib.sjs module located at src/main/ml-modules/root in the project. The thesaurus entries are found in the src/main/ml-data/thesaurus/example.xml file which is loaded into the application’s content database when the application is deployed. If you would like to try running the tests in this application, you can deploy it by cloning this repository and running the below commands. The value of mlPassword should be set to that of your MarkLogic admin user’s password.

cd examples/getting-started
echo "mlPassword=admin" > gradle-local.properties
./gradlew -i mlDeploy

Adding marklogic-unit-test to an existing project

marklogic-unit-test can be added to an existing ml-gradle project by adding the following configuration to the project’s build.gradle file:

dependencies {
  mlBundle "com.marklogic:marklogic-unit-test-modules:1.5.0"
}

This uses the ml-gradle bundle feature for sharing and reusing MarkLogic modules. After adding the above configuration (which you may instead add to an existing dependencies block in your build.gradle file), run the following task to install marklogic-unit-test into your MarkLogic application:

./gradlew -i mlLoadModules

If you would like to run your marklogic-unit-test tests via Gradle, you’ll also need to include the following at the top of your build.gradle file:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath "com.marklogic:marklogic-unit-test-client:1.5.0"
  }
}

The above configuration enables the ml-gradle mlUnitTest task to access the marklogic-unit-test-client library, allowing it to run your marklogic-unit-test tests.

If you are not using ml-gradle, you will need to manually add the marklogic-unit-test modules to your project. Please see the marklogic-unit-test releases for zip files containing these modules.

Writing a test

marklogic-unit-test will be used to verify the behavior of the lookupTerm function in the lib.sjs module. The assertions will be based on the known contents of the thesaurus file at src/main/ml-data/thesaurus/example.xml in the project.

Configuring where test modules are located

Before writing a test module, we need to perform a one-time step to configure the project so that test modules can be stored in a directory separate from the one containing application modules. This allows for the application to be deployed to non-development environments without including any of the test modules. Open your project’s gradle.properties file and add the following property:

mlModulePaths=src/main/ml-modules,src/test/ml-modules

By default, ml-gradle will only load modules from src/main/ml-modules. The above configuration allows for loading modules from src/test/ml-modules as well (note that you can choose any directory path you wish). This configuration can later be overridden via an environment-specific Gradle properties file to only load from src/main/ml-modules.

Next, create a src/test/ml-modules/root/test/suites directory in your project. All test modules will be stored in child directories of this directory.

Writing a test module

marklogic-unit-test requires test modules to have a URI beginning with /test/suites/(name of suite). Test files must therefore be stored in the src/test/ml-modules/root/test/suites/(name of suite) directory.

A test suite can have any name; for this example, we will use “thesaurus” as the name. Test modules can have any name as well with a few exceptions for setup and teardown modules; those exceptions are covered in the guide for writing tests. We will use “simple-test.sjs” for this example, so we create a file at src/test/ml-modules/root/test/suites/thesaurus/simple-test.sjs with the following initial content:

const test = require("/test/test-helper.xqy");
const lib = require("/example/lib.sjs");

The first line above imports the marklogic-unit-test module containing dozens of useful assertion functions; every test module will need this imported. The second line imports the library module that we wish to verify.

Next, add the following text to the file:

const result = lib.lookupTerm("Car");
[
  test.assertEqual("Car", result.term),
  test.assertEqual(1, result.entries.length),
  test.assertEqual(3, result.entries[0].synonyms.length, "3 synonyms are expected for 'Car'.")
];

The above code will invoke the lookupTerm function that we wish to test with a term that we know is in the application’s thesaurus. Each assertEqual function call - along with every other assertion function in marklogic-unit-test - will return a success or failure. The test then returns an array of these successes and failures. The different approaches for running tests know how to collect these results and display how many tests passed and how many failed.

Configuring a connection to MarkLogic

In order to run the test we’ve written, the test must be loaded into a modules database in MarkLogic. Both loading and running the test requires connecting to a MarkLogic App Server. ml-gradle supports both of these tasks, but a connection must be configured so that ml-gradle knows which App Server to connect to and how to authenticate.

By default, ml-gradle will use either the App Server port defined by the mlTestRestPort property if set, or else it will use the mlRestPort property. It will also use the REST API server connection properties for controlling how ml-gradle authenticates with the App Server. Note that the use of mlTestRestPort is optional; see the ml-gradle docs for information on whether you want to use this feature in your project.

In the case of our example project, the following properties in gradle.properties are used to configure a connection for loading and running tests:

mlHost=localhost
mlRestPort=8024
mlUsername=admin
mlPassword=this value will be set in gradle-local.properties

For more information on configuring the connection, please see this example project in ml-gradle.

Loading tests

Now that we’ve written a test and configured a connection to MarkLogic, we are ready to load the test into our application’s modules database. ml-gradle offers a variety of tasks to accomplish this with mlLoadModules being the simplest one. However, having to execute this task every time a test module is updated slows down the development cycle. To address this, ml-gradle provides support for watching for module changes and automatically loading them via the mlWatch task. It is recommended to execute this in a terminal window and leave it running while you create and modify your modules:

./gradlew -i mlWatch

The Gradle “-i” flag for info-level logging results in the filename of each module being logged when it is loaded.

Once you execute either mlWatch or mlLoadModules, your test will be ready to be run.

Running a test

marklogic-unit-test provides several ways to run tests. We will look at the two primary ways to run the test module we just wrote and loaded into our application’s modules database.

First, tests can be run via the ml-gradle mlUnitTest task, as long as you have included the marklogic-unit-test-client dependency in your build.gradle file as shown at the beginning of this guide:

./gradle -i mlUnitTest

You should see output like this:

> Task :mlUnitTest
Constructing DatabaseClient that will connect to port: 8024
Run teardown scripts: true
Run suite teardown scripts: true
Run code coverage: false
Running all suites...
Done running all suites; time: 11ms

1 tests completed, 0 failed

To see the output when a test fails, change one of the assertions in the simple-test.sjs file so that an incorrect value is expected. If you have the ml-gradle mlWatch task running, it will be loaded after you save the file; otherwise, run the mlLoadModules task again. Then run ./gradlew mlUnitTest to re-run the test, and you will see output like this:

> Task :mlUnitTest FAILED
Constructing DatabaseClient that will connect to port: 8024
Run teardown scripts: true
Run suite teardown scripts: true
Run code coverage: false
Running all suites...
Done running all suites; time: 15ms

simple-test.sjs > simple-test.sjs FAILED
    3 synonyms are expected for 'Car'.; expected: 4 actual: 3

marklogic-unit-test also provides a simple web interface for running tests. You can access this in your web browser via the port of your application’s REST API server and the path /test/default.xqy. For example, for the example project, you can access it at http://localhost:8024/test/default.xqy, assuming that your MarkLogic server is accessible at “localhost”. You can select the tests you wish to run and click on “Run Tests”.

Summary

This guide has covered the following topics:

  1. How to include marklogic-unit-test in your project.
  2. How to write a test.
  3. How to load a test.
  4. How to run a test.

With the above information and the references on writing tests and running tests, you can now start writing tests for the library modules in your application, ensuring that you can quickly enhance your application without breaking any existing functionality.