Testing while coding is always a good practice - You can keep an eye on what is not working and find regressions while adding value with new code. This becomes even more important as your team grows and your Nuxeo Studio projects become more complex. Thanks to Nuxeo Studio's Branch Management and Nuxeo CLI, you can build more imaginative code, scale your team conveniently, find bugs as you develop, and validate your Studio project.

Plug Nuxeo CLI Into Your Nuxeo Online Services (NOS) Account

Since Nuxeo CLI 1.5, you can connect your project with your NOS account and interact with it via the CLI. We've just started with this support and a lot more is coming in the next few weeks. But this CLI is already very useful, so keep reading!

First, let's connect them together. Using your favorite terminal emulator, go to the root folder of your project (or create a new one) and run:

$ nuxeo studio

You'll be prompted for your NOS credentials and your project’s symbolic name. (Don’t worry, we do not store them. We just use tokens afterwards.)

Run nuxeo studio as follows:

$ nuxeo studio
     info You are going to link a Studio project to this project.q
? NOS Username: akervern
? NOS Password: ********
? Studio Project: akervern-SANDBOX

After providing your credentials and Studio project your Maven dependencies tree will be updated. How you answer the last question will depend on your Maven's global settings file. Studio Maven's repository is protected and you must be authorized to download your artifacts and have access to the Internet. Nuxeo CLI will add your NOS Credentials to nuxeo-studio server if they are not already registered. And I insist: you MUST read and follow Maven's Password Encryption Guide before going further; having your password in clear text is...BAD.

If you are afraid when it asks if you want to override your settings.xml file, check this out:

 conflict ../../../../.m2/settings.xml
? Overwrite ../../../../.m2/settings.xml? (Ynaxdh)
  y) overwrite
  n) do not overwrite
  a) overwrite this and all others
  x) abort
  d) show the differences between the old and the new
  h) Help, list all options
  Answer:

I recommend you show the differences (d) first, in order to ensure that the modified lines are correct. Then, if everything looks ok, you can overwrite (y) with the changes.

Create a Simple Scripting Operation

Next, let's create a simple Automation Script in our Studio Project. Open Nuxeo Studio, and create an Automation Scripting Operation called smartest_thing using void as both input and output.

The content is trivial:

function run(input, params) {
  return 5;
}

Don't forget to save this operation. The next step will certainly be more interesting.

Unit Tests in Studio

Let's assume that you have some basic knowledge about Nuxeo testing and have read a few lines about our Junit based Unit Test Framework. Also, if you've already read my documentation on How to Test a Studio Bundle, you are ready to go, and have my gratitude for reading it through!

We are going to use our new annotation PartialDeploy (available since 9.1 or 8.10-HF04), that lets you select exactly which contributions you want to deploy during your test regardless of the requirements. It comes with some pre-filled classes to help you deploy most of what you can configure in Studio:

  • org.nuxeo.runtime.test.runner.TargetExtensions.ContentModel: Deploys Schemas, Document Types, Life Cycle, Directories and Versions Policy.
  • org.nuxeo.runtime.test.runner.TargetExtensions.ContentTemplate: Deploys everything from ContentModel and Content Templates.
  • org.nuxeo.runtime.test.runner.TargetExtensions.Automation: Deploys everything from ContentModel, Automation Chain and Automation Scripting.

Test an Automation Scripting Operation

First, let's bootstrap an empty test:

$ nuxeo bootstrap test-empty
     info Installation of single-module is skipped.
     info You'll be prompted for generation of:
     info   blog-project-core: test-empty

   create Generate Module: blog-project-core

   create Generating Test empty
     info   Parameters: Unit test package, Unit test class name, Using feature
? Unit-Test package: org.nuxeo.sample
? Unit-Test class name: MySmartestOperation
? Using Feature: AutomationFeature
     info Maven dependency: org.nuxeo.runtime:nuxeo-runtime-test:::test
     info Maven dependency: org.nuxeo.ecm.platform:nuxeo-platform-test:::test
     info Maven dependency: org.nuxeo.ecm.platform:nuxeo-platform-audit-core::test-jar:test
     info Maven dependency: org.nuxeo.ecm.automation:nuxeo-automation-test:::test
    force pom.xml
   create src/test/java/org/nuxeo/sample/MySmartestOperation.java
     info You can start editing code or you can continue with calling another generator (nuxeo bootstrap [<generator>..])

Using your preferred IDE, open the unit test class MySmartestOperation. Now we are going to align the test to make sure that our operation is correctly returning 5.

Note that if you want to test something more 'real', don't forget to deploy the actual project bundle as well.

package org.nuxeo.sample;

import static org.junit.Assert.assertEquals;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.nuxeo.ecm.automation.AutomationService;
import org.nuxeo.ecm.automation.OperationContext;
import org.nuxeo.ecm.automation.OperationException;
import org.nuxeo.ecm.automation.test.AutomationFeature;
import org.nuxeo.ecm.core.test.DefaultRepositoryInit;
import org.nuxeo.ecm.core.test.annotations.Granularity;
import org.nuxeo.ecm.core.test.annotations.RepositoryConfig;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;
import org.nuxeo.runtime.test.runner.PartialDeploy;
import org.nuxeo.runtime.test.runner.TargetExtensions;

/**
 * Empty Unit Testing class.
 * <p/>
 *
 * @see <a href="https://doc.nuxeo.com/corg/unit-testing/">Unit Testing</a>
 */
@RunWith(FeaturesRunner.class)
@Features(AutomationFeature.class)
@RepositoryConfig(init = DefaultRepositoryInit.class, cleanup = Granularity.METHOD)
// !! Notice your Project symbolic name is prefixed with "studio.extension."
@PartialDeploy(bundle = "studio.extensions.akervern-SANDBOX", extensions = TargetExtensions.Automation.class)
public class MySmartestOperation {

    @Inject
    protected AutomationService automationService;

    @Test
    public void assertWeAreSmart() throws OperationException {
        // !! Notice the Scripting name is prefixed with "javascript."
        Object res = automationService.run(new OperationContext(), "javascript.smartest_thing");
        assertEquals(5, res);
    }
}

And voila! You just wrote your first unit test validating the behavior of your Automation Scripting Operation. Welcome to TDD!

Bonus Point

If you are a part of a "Clean Deployment Aficionados" team, someone who loves to do things well, or just someone who follows best practices, you probably already have a Nuxeo Package in your project's modules. If not, get started by running nuxeo bootstrap package and all members of your Clean Deployment Aficionados team will love you. Then, you can build your project and open the package file, located in {your-project}-package/target/{your-project}-1.0.zip. Take a look inside. You can see that it now contains your Studio project jar file. You are now able to install your custom Java bundles and the Studio project together. Less deployment complexity means more time to enjoy adult BEvERages...or soda!