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 reusability
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:
Also you have to add a dependency to
nuxeo-runtime-test having as version at least 1.6.1-SNAPSHOT. Here is the maven dependency you should put in your pom:
If you want to use core features then you need to put dependency to
nuxeo-core-test (instead of the one on the
runtime-test). If you need platform features you need to use
nuxeo-platform-test; and if you want all nuxeo features use
Note that the Nuxeo test framework requires a Nuxeo platform having as version at least 5.3.1-SNAPSHOT.
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.
Here is a list of the most important features:
- WebDriver Feature
- Runtime Feature
- Jetty Feature
- Core Feature
- Platform Feature
- WebEngine Feature
- Nuxeo Distribution Feature
To configure your test to use a specific test feature you should 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 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.
Test features may requires 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 fetaures declaration in the following code is useless:
CoreFeature requires the
RuntimeFeature (and thus includes it automatically). So, the correct usage of the
As I said before each feature provides its own configuration annotations and a list of services or objects available through Guice injection.
In the next sections I 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.
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.
The WebDriver feature is a top level feature so it is not requiring other features.
- @Browser- Configure 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)
- 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.
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 to 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 instatiated 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 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. page) of your application like the log-in/log-out functionality, the menu etc. So typically a home psge 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.
Important Note: 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 ensure 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.
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.
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.
- @Deploy - deploy 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 - deploy 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.
All Nuxeo services deployed using
@LocalDeploy annotations will be available through Guice injector.
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:
@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
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.
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.
The Jetty feature requires Runtime Feature
- @Jetty - configure 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.
If no custom configuration is specified the default one will be used (that also contains data-source JNDI bindings for various Nuxeo services).
Starts Nuxeo server + embedded jetty on port 9090:
This feature deploys the required nuxeo core bundles and resources and configure a repository to be used by tests.
The feature requires Runtime Feature
- @RepositoryConfig - configure 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 - configure a suite test to run the same test over multiple repository configurations
- @RepositoryConfig - an array of repository configuration annotations.
- RepositorySettings - the repository configuration. This is the configuration built from the annotations.
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 cam be used to run test suites over various repository backends. Example of utilisation:
This test will run the test above (i.e. SimpleSession) once for each of the backends H2, JCR and POSTGRES.
This feature extends the Core Feature and adds deployment of basic Nuxeo services like directories, user manager etc.
The feature requires Core 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.
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.
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
A simple feature that extends Platform Feature and adds Nuxeo Automation bundles (org.nuxeo.ecm.automation.core and org.nuxeo.ecm.automation.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.
- @LogCaptureFeature.FilterWith - configures the filter the logs has to match to be part of the results.
- LogCaptureFeature.Result logCaptureResult - the result containing the captured logs.
A simple feature that extends WebEngine Feature and adds Nuxeo Theme deployment.
The feature requires WebEngine Feature
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.
This is a top level feature.
- @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 - put maven offline
- String updatePolicy - snapshot update policy to be used by maven. Default is "daily"
All deployed Nuxeo services.
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 doircetory configurator in intiialziation part which will be called when framework will be started and before bundles are deployed. No need to unregister the configurator.