Skip to end of metadata
Go to start of metadata

Overview

The Nuxeo Test Framework is based on JUnit4 and provides features like:

  • Test configuration through annotations,
  • Embedded Nuxeo Runtime,
  • WebDriver support (and optional Concordion support),
  • Guice injection of Nuxeo services and other configuration objects (injection is available only in test classes),
  • High extensibility and test configuration re-usability.

At the time of this writing, the test framework depends on JUnit 4.7, Guice 1.0 and WebDriver 0.9.7376.

To use the test framework you must launch your test using the Nuxeo JUnit4 runner: FeaturesRunner.

Example:

The org.nuxeo.runtime:nuxeo-runtime-test module is required.

Maven dependency to add in your POM

If you want to use core features, then you need to put a dependency to org.nuxeo.ecm.core:nuxeo-core-test .
If you want to use platform features, then you need org.nuxeo.ecm.platform:nuxeo-platform-test .
And if you want all Nuxeo features, then you need org.nuxeo.ecm.platform:nuxeo-features-test .

Test Features

To configure the way the tests are 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.

Here are some commonly used features:

See the org.nuxeo.runtime.test.runner.RunnerFeature implementations for a full list.

To configure your test to use a specific test feature, you must add the @Features annotation on your test class and specify the feature (or the list of features you need). Example:

Example on using multiple features:

The list of configuration annotations provided by features will be precised for each feature when available.

Injection

Injection is available only in test classes. You cannot use injection in Nuxeo code since the Nuxeo Platform doesn't support injection yet. The FeaturesRunner instance is provided by default through Guice injection so in your test case you can access the runner in that way:

Features may provide more injectable objects. The complete list of these objects will be precised for each feature when available.

Features

Test features may require other test features. This means that using a feature will automatically include all the required features. So, it's redundant to specify features that are anyway included by other features specified on your test. For example the features declaration in the following code is useless since the CoreFeature requires the RuntimeFeature (and thus includes it automatically).

So, the correct usage of the CoreFeature is:

As said before each feature provides its own configuration annotations and a list of services or objects available through Guice injection.

In the next sections we will describe each feature and specify how it can be used, which are the annotations provided provided for configuration and which are the injectable objects provided by the feature.

WebDriver Feature

The main purpose of this feature is to start a WebDriver before the tests are launched and stop it at the end of the tests.

Requirements

The WebDriver feature is a top level feature so it is not requiring other features.

Configuration

  • @Browser- to configure the WebDriver instance.
    • BrowserFamily type - the driver type. Can be one of: FIREFOX, IE, CHROME, HTML_UNIT, HTML_UNIT_JS.
    • Class factory - optional driver factory class to create custom drivers.
  • @HomePage- to specify the home page (the entry point in the site to test)
    • Class page - the page to use as the home page
    • String url - the URL to be bound to the home page (the site home URL)

Injectable Objects

  • The WebDriver instance.
  • The Configuration instance used to configure WebDriver. Can be used to introspect WebDriverFeature configuration.
  • The WebPage instance of the home page. Must be used to get the entry point to the site.

Usage

WebDriver should be used in conjunction with Page Provider pattern. Below you can see a simple example of testing the Google search. First, we need to create a page object corresponding to the Google home:

When using the page factory pattern you should extends the abstract WebPage provided by the Nuxeo test framework which already has the driver instance inject inside. The WebPage abstraction is also providing helper methods to quickly access WebDriver find methods (you also have a "wait until element is found" mechanism to block the test until the page is loaded and the given element is found). From a WebPage object you can instantiate other page objects by calling getPage(MyPage.class). Pages instantiated this way will be injected using the Guice injector of the runner. Note that pages retrieved this way are cached so you don't need to cache them in a local variable in the test method.

When using the page factory pattern you must expose your site's functionality through page objects. The page objects are not the equivalent of a real web page. They should be used to map services offered by a WebPage and not the entire web page. For example you can design your home page to allow access to other standard 'sections' (i.e. pages) of your application like the log-in/log-out functionality, the menu etc. So typically a home page will provide methods to access sub pages of your WEB application and/or global functionality existing on the home page.

In our example the home page is providing a way to do a search.

Icon

When testing web applications you must put all the details of web page mapping (like accessing elements in the DOM, doing clicks, filling forms etc.) in your web page implementation. You must not directly access these details from the test itself. This technique ensures that when you refactor a functionality of your web site you should only update the corresponding web page in your tests and not all the tests classes.

Now that we defined our home page, let's define the search result page that is returned by the search method. This page give access to the search result.

The search result page provides a method that returns the first link text in the search result.

Now we can write our Google search test like this:

By using WebDriver Feature in conjunction with WebEngineFeature you can test Nuxeo WebEngine modules that are deployed by the test framework without the need to point to an external URL. We will see this later in WebEngine Feature usage.

Runtime Feature

This is one of the most useful features when testing Nuxeo applications. The main purpose of this feature is to start a Nuxeo Test Server before the tests are launched and stop it at the end of the tests. So all the tests launched by the test unit will run inside the same instance of Nuxeo Runtime.

Requirements

The Runtime feature is a top level feature so it is not requiring other features. After starting the server all runtime bundles are deployed by the feature.

Configuration

  • @Deploy - deploys a bundle (or an XML component from the given bundle) into the running server.
    • String[] value - the path of the bundle (or contribution). The path format is "symbolicName:path" where symbolicName is the bundle symbolic name and the optional path is a path relative to the bundle root of the XML component to deploy. If no path is specified the entire bundle will be deployed.
      Example: "org.nuxeo.runtime", "org.nuxeo.runtie:OSGI-INF/my-component.xml" etc.
  • @LocalDeploy - deploys a test XML component as part of an existing bundle into the running server. This is useful to deploy test contributions that are not part from a real bundle.
    • String[] value - same as for the @Deploy annotation. The difference is that the symbolicName will be the one of the target bundle, and the path is required and should point to a test resource.

Injectable Objects

All Nuxeo services deployed using @Deploy and @LocalDeploy annotations will be available through Guice injector.

Usage

Here is an example on how to test for the presence of a Nuxeo service:

Here is another example on how to use the @Deploy annotations to deploy additional bundles and components:

The @Deploy annotations will install the core schema bundle into the running server and the @LocalDeploy will install the test resource located at OSGI-INF/my-test-contribution.xml (the current class loader is used to locate the resource) into the existing nuxeo-runtime bundle.

Icon

You must never deploy bundles using the RuntimeHarness API. You must always use @Deploy annotations to do this. However this can be useful when writing custom features but use it with care.

Jetty Feature

The main purpose of this feature is to start a Jetty server embedded in Nuxeo Test Server before the tests are launched and stop it at the end of the tests. This feature extends the Runtime feature and adds the nuxeo-runtime-jetty-adapter to the deployments.

Requirements

The Jetty feature requires Runtime Feature

Configuration

  • @Jetty - configures the Jetty server
    • String host default "localhost" - the host to bind to.
    • int port default 8080 - the port to bind to.
    • String config - optional attribute to specify a custom jetty.xml configuration file.
    • boolean propagateNaming default false - allow runtime fixture get access to the web-app naming context

If no custom configuration is specified the default one will be used (that also contains data-source JNDI bindings for various Nuxeo services).

Injectable Objects

None.

Usage

Starts Nuxeo server + embedded Jetty on port 9090:

You may also want your request being enrolled in transaction. Then you should use the Jetty Transactional Feature instead. You should in that case instruct the JVM to use Nuxeo's naming factory by adding the following src/test/resources/jndi.properties file in your project.

Transactional Feature

If you want your tests to be ran in a transaction context, you should use the Transactional Feature.

Requirements

Requires the Runtime Feature and should be installed before the Core Feature. So your test should looks like

Configuration

  • @TransactionalConfig
    • boolean autostart true - injects a new transaction before executing each test

By default, the transactional feature configures the core feature to use the pooling repository factory. If you're using the repository config annotation on your test, you should always have the repository factory class attribute specified as follow in your test.

Core Feature

This feature deploys the required nuxeo core bundles and resources and configure a repository to be used by tests.

Requirements

The feature requires Runtime Feature

Configuration

  • @RepositoryConfig - configures a Nuxeo Repository
    • BackendType type default H2 - the type of the repository backend. Available options: H2, POSTGRES, JCR.
    • Class init - a RepositoryInit class to be used to initialize the repository content.
    • Granularity cleanup default CLASS - the scope of the initialization done by the init class. Can be one of CLASS or METHOD. If METHOD is used the intialization will be done before each test method call.
  • @RepositoryConfigs - configures a suite test to run the same test over multiple repository configurations
  • @RepositoryConfig[] - an array of repository configuration annotations.

Injectable Objects

  • RepositorySettings - the repository configuration. This is the configuration built from the annotations.

Usage

This will start a nuxeo repository on top of H2 backend will create a CoreSession using the "Administrator" user and will initialize the repository using the DefaultRepositoryInit initializer only once (CLASS level) before running the test.

Core Test Suite

Nuxeo Core provides a special runner (i.e. MultiNuxeoCoreRunner) that can be used to run test suites over various repository backends. Example of use:

This test will run the test above (i.e. SimpleSession) once for each of the backends H2, JCR and POSTGRES.

Platform Feature

This feature extends the Core Feature and adds deployment of basic Nuxeo services like directories, user manager etc.

Requirements

The feature requires Core Feature.

Configuration

None

Injectable Objects

None

WebEngine Feature

This feature extends the Platform Feature and adds deployment of WebEngine core bundles (including Administration module). Also it is adding the WebDriver and Jetty features. You should use this feature when testing WebEngine UI.

Requirements

The feature requires Platform Feature, WebDriver Feature and Jetty Feature

This feature may not work directly from Eclipse since it requires on its classpath the final WebEngine JARs - as they are built with Maven - to have generated type definitions in the META-INF directory. This will be fixed later in WebEngine.

Configuration

None

Injectable Objects

None

Usage

This test will start a nuxeo with Jetty enabled at port 8082 - so WebEngine root will be located at http://localhost:8082. The repository is of type H2 and the @HomePage used by WebDriver feature will point to the embeded WebEngine root.

You can see that this test respect the page factory pattern we discussed above: the web mapping details are done inside the web page implementations. The test is not aware about the page structure - it is only using well named methods to access different fucntionality.

If you want to have a look on all these classes you can find this test in nuxeo-features-test

Automation Feature

A simple feature that extends Platform Feature and adds Nuxeo Automation bundles (org.nuxeo.ecm.automation.core and org.nuxeo.ecm.automation.feature).

Dummy Client Login Feature

A feature to login any username without any check against usermanager. Useful for low levels unit tests such as in directory, wherer it's not possible to add a dependency on usermanager or for test that don't setup directories. Then each authenticated users will be available as usual in the stack ClientLoginModule.getCurrentPrincipal();

By default only the group Everyone is added to each users and it is not possible to add other groups.

To use it, just add the following dependency : 

Requirements

Add the following dependency to use it : 

 

Configuration

None

Injectable Objects

None

Usage

LogCapture Feature

A feature to make assertion on Log4j output.

A Log4j appender is initialized and configured with the running Log4j instance. When any Log4j log method is called, the appender captures the logs if the logs match the configured filter. It's then possible to get the captured logs or assert if there is any captured log.

Requirements

None

Configuration

  • @LogCaptureFeature.FilterWith - configures the filter the logs has to match to be part of the results.

Injectable Objects

  • LogCaptureFeature.Result logCaptureResult - the result containing the captured logs.

Usage

Theme Feature

A simple feature that extends WebEngine Feature and adds Nuxeo Theme deployment.

Requirements

The feature requires WebEngine Feature.

Configuration

None

Injectable Objects

None

Nuxeo Distribution Feature

This feature is still in development so it is not usable for now.
A feature that replace the RuntimeFeature to run tests on real Nuxeo distributions. This feature is using nuxeo-distribution-tools to build and launch a Nuxeo distribution.

Requirements

This is a top level feature.

Configuration

  • @NuxeoDistribution - the distribution configuration
    • String profile - the profile name. Example: core-1.6.1-SNAPSHOT, core-1.6.0 etc.
    • String config - custom profile configuration - to be used to specify custom profiles.
    • String host - the host to listen to.
    • String port - the HTTP port to bind to.
    • boolean useCache - whether ot not to cache the build. The default is true.
    • boolean offline - puts Maven offline.
    • String updatePolicy - snapshot update policy to be used by Maven. Default is "daily"

Injectable Objects

All deployed Nuxeo services.

Usage

This can be used for example to decorate existing test and launch them in an integration environment over a real Nuxeo distribution.

This test decorates an existing test (that may have different feature configuration to be run by developer). The DistributionFeature will remove the original features and configurations and will run the test under the integration test configuration.

Creating Custom Features

When you are writing generic services that you export to other bundles you may want to provide a specific feature for you services to help others testing code depending on your service. In most cases the feature will only extend an existing feature by adding new deployments. This is the case for example of the Platform Feature (not that a feature must implement the RunnerFeature interface - in our case we extend SimpleFeature which is an empty feature):

To list the required you must use the @Features annotation on your own feature. You noticed that @Deploy annotation is supported on features too (this is not the case of other annotations - in fact it depends on how the feature that provide the annotation is scanning for them).

If you need more control you can refer to sources and/or existing feature implementations. Below you can find some typical use case you may need when implementing a feature:

Custom Configuration Annotations and Guice Injection Providers

Usually annotations should be collected in the initialize method. The configure method is called after the start method and before the Guice injector is created.

Dynamic Bundle Deployment

As you see you must add the requirement on the RuntimeFeature since you need to access to bundle deployment which is provided by this feature.

Even if it is possible - you must not access the underlying Runtime Harness directly from the test - this will make your test non portable. You will not be able to use it with Distribution Feature for example. The Runtime Harness is safe to be accessed only from features that depends on Runtime feature.

Patch Nuxeo Server Home Directory

Again, this feature is depending on the Runtime Feature. It is adding a working directory configurator in the initialization part which will be called when the framework is started and before bundles are deployed. No need to unregister the configurator.