In this how to, you will learn how to unit test a Studio Bundle from a Java Project.
Optional - Creating a Bare Project
If you already have a project you can go to the next section.
- Install Nuxeo CLI.
Create a new bare project:
mkdir my-project && cd $_ nuxeo bootstrap
Do not hesitate to read the dedicated page to fill in the inputs.
Check that everything is working
mvn install
And the process must be exited without any errors.
Add Your Connect Credentials to Maven
Adding your credentials allows Maven to be authenticated in Studio while trying to grab your project.
- Open
~/.m2/settings.xml
file with your preferred text editor Add the following
server
node:<settings> ... <servers> ... <server> <id>nuxeo-studio</id> <username>your_nos_username</username> <password>your_nos_token</password> </server> </servers> </settings>
It is recommended that you don't leave your token directly in the XML file. You can encrypt it by following the official password encryption guide.
Adding Nuxeo Studio as Maven Repository
- Open
./pom.xml
file with your preferred editor. Check if the following
repository
is present. If not, add it:<project> ... <repositories> ... <repository> <id>nuxeo-studio</id> <url>https://connect.nuxeo.com/nuxeo/site/studio/maven</url>; <releases> <enabled>true</enabled> </releases> <snapshots> <updatePolicy>always</updatePolicy> <enabled>true</enabled> </snapshots> </repository> </repositories> </project>
Adding Studio Bundle Dependency
- Open Nuxeo Studio and the project that you want to unit test.
Pick your Maven GAV information in Studio. Go to
Settings
>Application Information
.groupId
: is theMaven Group
fieldartifactId
: is theMaven Artifact Id
fieldversion
: follows this conventionX.Y.W--branch-SNAPSHOT
.
For example, if you are working on version
0.0.2-SNAPSHOT
(openSource Control
>Branch Management
) and on branchmaster
, the Maven version will be:0.0.2--master-SNAPSHOT
. If you want a released version, the version pattern is much easier:0.0.2
.Then edit the
./pom.xml
file to add the dependency management. It's recommended to set the dependency's version in the parentpom
of your project. For more information, you can read Dependency Management in the official Maven guide.<project> ... <dependencyManagement> ... <dependencies> <dependency> <groupId>STUDIO_GROUPID</groupId> <artifactId>STUDIO_ARTIFACTID</artifactId> <version>STUDIO_VERISON</version> </dependency> </dependencies> </dependencyManagement> </project>
Add a direct dependency in the
./myproject-core/pom.xml
file, or thepom.xml
of your module in which you'll want to write the unit tests.<project> ... <dependencies> <!-- Note that we do not need the version here, it's inherited from the parent pom.xml file. --> <dependency> <groupId>STUDIO_GROUPID</groupId> <artifactId>STUDIO_ARTIFACTID</artifactId> </dependency> </dependencies> </project>
Understanding @PartialDeploy Annotation
Once you've understood the @Deploy annotation, you will have noticed that deploying a Studio Bundle is not as straightforward as it sounds because of the way the Studio bundle contributions are packed. The idea behind this annotation is to allow you to select which contributions you want to deploy in your unit test.
For example, you may want to deploy only Schema, Document Type and custom event listeners to make sure that your business rules are correctly applied. Or, you may simply want to verify that your Automation Scripting is doing what you'd expect.
Testing Content Model
- Assuming you have already created a DocType, "MyCustomDoc", in your Studio Project...
Add the following dependency to
./myproject/pom.xml
file:<dependencies> ... <dependency> <groupId>org.nuxeo.runtime</groupId> <artifactId>nuxeo-runtime-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.nuxeo.ecm.platform</groupId> <artifactId>nuxeo-platform-test</artifactId> <scope>test</scope> </dependency> <dependencies>
Create a new test case class
./myproject-core/src/test/java/com/bigcorp/MyDocType.java
Add the following content:
package com.bigcorp; import static org.junit.Assert.assertNotNull; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.test.DefaultRepositoryInit; import org.nuxeo.ecm.core.test.annotations.RepositoryConfig; import org.nuxeo.ecm.platform.test.PlatformFeature; 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; @RunWith(FeaturesRunner.class) @Features(PlatformFeature.class) @RepositoryConfig(init = DefaultRepositoryInit.class) // Note that the dedicated Annotation takes the bundle name and the whitelisted target extensions list @PartialDeploy(bundle = "studio.extensions.YOUR_PROJECT_NAME", extensions = { TargetExtensions.ContentModel.class }) public class MyDocTypeTest { @Inject CoreSession session; @Test public void testMyDocumentType() { DocumentModel contract = session.createDocumentModel("/default-domain/Workspaces", "firstContract", "Contract"); contract.setPropertyValue("contract:amount", 200L); DocumentModel document = session.createDocument(contract); assertNotNull(document.getId()); } }
- Make sure that you change the
YOUR_PROJECT_NAME
to the correct one. - And voila! The test should pass and you have only deployed what is necessary to test the
Content Model
category in Studio.
/META-INF
MANIFEST.MF
Bundle-SymbolicName:studio.extensions.XXXX
Testing Automation
- Assuming that you have already created an Automation Chain,
UpdateTaxes
, that calculatescontract:taxes
fromcontract:amount
... - Add the following dependency to
./myproject/pom.xml
file:<dependencies> ... <dependency> <groupId>org.nuxeo.ecm.automation</groupId> <artifactId>nuxeo-automation-test</artifactId> <scope>test</scope> </dependency> <dependencies>
- Create a new test case class
./myproject-core/src/test/java/com/bigcorp/UpdateTaxesTest.java
Add the following content:
package com.bigcorp; 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.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; 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; @RunWith(FeaturesRunner.class) @Features({ AutomationFeature.class }) // In the case of Automation, we whitelisted all the Automation extensions (which also contains Content Model) @PartialDeploy(bundle = "studio.extensions.YOUR_PROJECT_NAME", extensions = { TargetExtensions.Automation.class }) public class UpdateTaxesTest { @Inject AutomationService automationService; @Inject CoreSession session; @Test public void taxesCalculationTest() throws OperationException { DocumentModel doc = session.createDocumentModel("/", "test-file", "Contrat"); doc.setPropertyValue("contract:amount", 200); doc = session.createDocument(doc); OperationContext ctx = new OperationContext(session); ctx.setInput(doc); doc = (DocumentModel) automationService.run(ctx, "UpdateTaxes"); assertEquals(11, doc.getPropertyValue("contract:taxes")); } }
- Verify that you've changed
YOUR_PROJECT_NAME
to the correct one.
Testing Automation Scripting
- Assuming that you have already created an Automation Script,
SumTaxes
, that returns the sum of all taxes... - Create a new test case class
./myproject-core/src/test/java/com/bigcorp/SumTaxes.java
Add the following content:
package com.bigcorp; import static org.junit.Assert.assertEquals; import java.util.stream.IntStream; 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.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; 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; @RunWith(FeaturesRunner.class) @Features({ AutomationFeature.class }) @PartialDeploy(bundle = "studio.extensions.YOUR_PROJECT_NAME", extensions = { TargetExtensions.Automation.class }) public class SumTaxes { @Inject AutomationService automationService; @Inject CoreSession session; private final int TAXES = 10; @Test public void sumTaxesTest() throws OperationException { final int NB_DOCS = 10; IntStream.range(0, NB_DOCS).forEach(this::createDocument); OperationContext ctx = new OperationContext(session); // Note that the Scripting name is prefixed by "javascript.". int taxes = (int) automationService.run(ctx, "javascript.SumTaxes"); assertEquals(NB_DOCS * TAXES, taxes); } protected void createDocument(int index) { DocumentModel doc = session.createDocumentModel("/", "test-file-" + index, "Contrat"); doc.setPropertyValue("contract:taxes", TAXES); session.createDocument(doc); } }
- Verify that you've changed
YOUR_PROJECT_NAME
to the correct one.
Writing Your Own TargetExtensions Class
If you need to partially deploy other contributions to a specific target, it's possible to create your own TargetExtension
class.
- Create a new class
./myproject-core/src/test/java/com/bigcorp/BigCorpExtension.java
Add the following content:
package com.bigcorp; import org.nuxeo.runtime.test.runner.TargetExtensions; public class BigCorpExtension extends TargetExtensions { @Override protected void initialize() { // Add whitelisted targets addTargetExtension("com.bigcorp.component", "target"); addTargetExtension("com.bigcorp.component", "target-2"); } }
- Add it to the list of extensions in the expected @PartialDeploy annotation like:
@PartialDeploy(bundle = "studio.extensions.YOUR_PROJECT_NAME", extensions = { BigCorpExtension.class, TargetExtensions.Automation.class })
- And that's it!