[Q&A Friday] How to Test Operations?


Fri 24 February 2012 By Laurent Doguin

Here's an interesting question from Narcis. He asks how he can test an operation. Quality is of paramount importance here at Nuxeo, as you may have gathered from a previous blog post. Most of us use TDD (Test-driven development), and hence rely heavily on unit tests. We try to make developers' lives easier by giving them different test cases or runners to use in their tests. Take a look at our documentation on unit tests.

As Benjamin said in his answer, there are a lot of examples in our source code. Let's go through one of them. I've chosen the simple LoginAsTest.

/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bstefanescu
*/
package org.nuxeo.ecm.automation.core.test;

import java.util.ArrayList;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.nuxeo.ecm.automation.AutomationService;
import org.nuxeo.ecm.automation.OperationChain;
import org.nuxeo.ecm.automation.OperationContext;
import org.nuxeo.ecm.automation.core.operations.FetchContextDocument;
import org.nuxeo.ecm.automation.core.operations.document.CreateDocument;
import org.nuxeo.ecm.automation.core.operations.login.LoginAs;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.platform.test.PlatformFeature;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.test.runner.Deploy;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;

import com.google.inject.Inject;

/**
* @author <a href="mailto:[email protected]">Bogdan Stefanescu</a>
*/
@RunWith(FeaturesRunner.class)
@Features(PlatformFeature.class)
@Deploy({ "org.nuxeo.ecm.automation.core",
    "org.nuxeo.ecm.automation.features",
    "org.nuxeo.ecm.platform.web.common", "org.nuxeo.ecm.platform.login" })
public class LoginAsTest {

    protected DocumentModel src;

    @Inject
    AutomationService service;

    @Inject
    CoreSession session;

    @Inject
    UserManager mgr;

    @Before
    public void initRepo() throws Exception {
        session.removeChildren(session.getRootDocument().getRef());
        session.save();

        src = session.createDocumentModel("/", "src", "Folder");
        src.setPropertyValue("dc:title", "Source");
        src = session.createDocument(src);
        session.save();
        src = session.getDocument(src.getRef());

        DocumentModel userModel = mgr.getBareUserModel();
        String schemaName = mgr.getUserSchemaName();
        userModel.setProperty(schemaName, "username", "Foo");
        ArrayList<String> groups = new ArrayList<String>();
        groups.add("administrators");
        userModel.setProperty("user", "groups", groups);
        userModel = mgr.createUser(userModel);
    }

    // ------ Tests comes here --------

    @Test
    public void testLoginAs() throws Exception {
        // change the user inside an operation chain.

        OperationContext ctx = new OperationContext(session);
        ctx.setInput(src);
        String origPrincipal = ctx.getPrincipal().getName();
        System.out.println(origPrincipal);
        OperationChain chain = new OperationChain("testloginas");
        chain.add(FetchContextDocument.ID);
        chain.add(LoginAs.ID).set("name", "Foo");
        chain.add(CreateDocument.ID).set("type", "Folder").set("name", "myfolder");
        DocumentModel doc = (DocumentModel)service.run(ctx, chain);

        Assert.assertEquals(origPrincipal, ctx.getPrincipal().getName());
        Assert.assertEquals("Foo", doc.getPropertyValue("dc:creator"));
    }

}

Let's start with the class annotations. You might be familliar with the @RunWith annotation as it figures in Junit4. The tests run in the specified class instead of the default Junit runner.

Moving to @Features. Here's an extract from our documentation:

To configure the way the test is launched the Nuxeo test framework is using the concept of test features. A test feature is a special class that is notified by the runner about the different life cycle events of the execution so that it may customize the way the test is setup or test methods are run. Test features are configurable through annotations. Each test feature is able to use its own defined annotations to configure the test. Also a feature may provide additional objects to be injected in tests using Guice.

And finally @deploy, one of the most important annotation since it allows you to deploy other nuxeo bundles. This is what we actually use in the different features. That's why you can inject the directory service or the user manager when you have the PlatformFeature. Here we deploy the automation bundles as well the web-common and login bundles to test the loginAs operation.

I previously mentioned injection. We use Guice to inject nuxeo services as well as other objects. This is also described in our documentation.

And finally you have the usual Junit annotations, @Before to execute code before a test, and @test to identify the test methods.

Don't hesitate to look at the range of features we have on offer. Here is a list of some of the most important ones:


  • WebDriver Feature

  • Runtime Feature

  • Jetty Feature

  • Core Feature

  • Platform Feature

  • WebEngine Feature

  • Nuxeo Distribution Feature


A special thanks to Damien Metzler and his team who contributed most of the junit4/Guice testing framework, and to Bogdan who integrated it into nuxeo-runtime.


Category: Product & Development
Tagged: Java, Q&A