Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

Rate Macro

Table of Contents

Introduction

Magnolia uses JUnit 4 tests and if dynamic mocks are required Mockito (new) for creating unit tests. Before we were using EasyMock for dynamic mocks. Independently from that make sure your tests match our conventions.

Mockito

...

Mockito is a more recent mocking library. We'll not bulk convert existing EasyMock-Tests as this would be to big an effort. Instead we set up the following rules:

  • all new tests requiring dynamic mocks use Mockito
  • whenever you touch (fix, adapt, complete) an existing test that's using EasyMock: convert it to Mockito

Setting up for Magnolia testing

There are some handy classes available when creating tests for your Magnolia functionality. To get them working in your Maven project, add the following dependencies to your project descriptor.

Code Block
titlepom.xml
    ...

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.4</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.8.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>info.magnolia</groupId>
      <artifactId>magnolia-core</artifactId>
      <version>${magnoliaVersion}</version>
      <type>test-jar</type>
      <scope>test</scope>
    </dependency>

    ...

Magnolia Mocking overview

For Independently from that make sure your tests match our conventions. Information on how to best migrate JUnit3-style tests to JUnit4 can be found here.

Magnolia Provided Testing Support

In general we try to use the slimmest possible approach in tests. For testing basic functionality that does depend on itself there is no need to use mock objects we . We can just test that method functionality straight away, but usually .

In cases we need to test objects that depend on other objects and there this is where we use this mock objects, beware . Beware that they can just return what you want but the code behind won't be executed.

Imagine you need to test something that depends on a repository, well you can 'mock' a repository and it's content without having to create the repository itself. You can extend your test class from MgnlTestCase that will setup the basic environment for you and then use something like:

Code Block
MockSession HierarchyManagersession hm = MockUtilSessionTestUtil.createAndSetHierarchyManagercreateSession(REPOSITORY"testWorkspace", CONTENTpropertiesString);

By doing this you can access to the nodes of you 'fake' repositoy as if it where real. But if you really need to use a real repository you can extend from RepositoryTestCase and use the methods declared in it.

Useful classes included in magnolia-core for building tests:

Here are some of the clases we have for Magnolia testing, you can find more classes in package info.magnolia.test 

...

TypeRemark
ComponentsTestUtil

Set default implementations or instances when IoC can't be used yet.

MgnlTestCaseSets up a basic environment for the test, loads beans and modules properties and initializes a mock context as the local context.
  • MockUtil - You can create mock objects and using createHierarchyManager you can build mock content based on a property file. The content can be force to be ordered and it allows you to create nodes, content, properties...
  • MockContent - emulates a Content object used by MockUtil
  • MockConext - emulates a context where you can set a mocked hierarchy manager
  • RepositoryTestCase - Can be used to test on a real repository. It will initialize and delete it when finished.
  • FactoryUtil - Class to allow various kinds of classes instantiations. Includes methods to convert content to beans.
Code Block

final SystemContext sysCtx = createStrictMock(SystemContext.class);

        sysCtx.setLocale(Locale.ENGLISH);

        FactoryUtil.setInstance(SystemContext.class, sysCtx);

What this example does is to use the EasyMock method createStrictMock that creates an instance of the SystemContext inteface meaning that it creates a mock object that implements this interface checking the order of method calls. It sets the property Locale to this new context and then uses the FactoryUtil class to register the new instance which will be returned by getSingleton()

Magnolia Mock Objects - Mock Content 

The customized mock objects that we provide to be able to setup an environment for Magnolia tests can be found in package info.magnolia.test.mock.

When creating a mock hierarchy manager you can either initialize it from a properties file:

Code Block
titlesample.properties
main@type = mgnl:content
main@uuid = 1

main/uuidLink@type = mgnl:contentNode
main/uuidLink@uuid = 2
main/uuidLink.MetaData.mgnl\:template = someParagraphName
main/uuidLink.MetaData.mgnl\:authorid = superuser
main/uuidLink.MetaData.mgnl\:activatorid = superuser
main/uuidLink.MetaData.mgnl\:title = myTitle
main/uuidLink.link1 = 3

main/linkTarget@type = mgnl:content
main/linkTarget@uuid = 3
main/linkTarget.prop1 = sub2value1
main/linkTarget.prop2 = sub2value2
main/linkTarget.prop3 = boolean:false

main/content@type = mgnl:contentNode
main/content@uuid = 4
main/content.value = Content Value
Code Block
titleCreateMockContentFromPropertiesFile.java
MockUtil.createAndSetHierarchyManager(ContentRepository.USERS, getClass().getResourceAsStream("sample.properties"));

or you can create a variable with the data or pass it to the method call directly:

Code Block
titleCreateMockContentFromString.java
public final String CONTENT = ""
+ "main/content@type = mgnl:contentNode"
+ "main/content@uuid = 4"
+ "main/content.value = Content Value";

MockUtil.createAndSetHierarchyManager(ContentRepository.USERS, CONTENT);

You may also directly instantiate a MockContent and then add the child Contents and or NodeData's as required

Code Block
titleCreateMockContentUsingAPI.java
MockContent page = new MockContent("page");
page.createContent("subpage", ItemType.CONTENT);
page.setNodeData("stringProperty", "HelloWorld");
RepositoryTestCaseSuperclass for Tests requiring access to a real jcr repo.
MockUtilUtil to create mock objects - especially MockContexts.
MockContextContext where you can add Sessions and set a User
MockNodeMock implementation of a jcr Node
MockContentMockImplemenation of a Content

 

What to choose when

Here's a few examples that should help to understand what approach should preferably be used in what situation:

General SetupSpecificsPreferred ApproachExample
Class under tests operates on JCR Nodefew calls to common methods of the NodeMockNode if it supports those calls, Mockito mock else 
 need a simple hierarchy of NodesMockNode if it supports those calls, Mockito mock else 
 need a simple hierarchy of Nodes but with several propertiesuse SessionTestUtil to instantiate MockSession + MockNodes from propertiesStream or String 
 need a complex hierarchy of Nodes, real NodeTypes or issue real queriesuse RepositoryTestCase 

Naming

A test class should end with Test. Typically, it will have the exact same name (and package) as the class it tests, with the Test suffix.

If you’re writing a test class meant to be re-used as a super class for other tests, make it actually abstract and that should suffice to exclude it from execution. (it would otherwise fail, because it likely has no @Test methods). Another pattern we've used in the past (but I'd like to get away from it) is suffixing with TestCase (e.g RepositoryTestCase).

Methods: since we’re using jUnit 4, there is no need to prefix method names with test. Choose a method name that describes what the test asserts. (fooDoesBarWhenX())

Self tests: if you need to test methods of the test class itself (e.g utility methods that the tests use need to be themselves to validate their behaviour), do that in a method called selfTest(). If there are multiple such test methods, refer to the point above.

If you’re testing a reusable test class, two options:
- don’t suffix it with TestCase, but just with Test and use selfTest() methods.
- if that’s not applicable, FooBarTestCaseSelfTest might seem a little over the top.

If you're testing an abstract class whose name is prefixed with Abstract, follow the same conventions. Name it AbstractFooTest. Add your @Test methods. If the test class isn't abstract, they'll be executed. Specify in the Javadoc if its meant to be reused for implementing tests for concrete Foo implementations. If the tests need a concrete an instance of Foo, implement it as an inner class of your tests and explicitly don't implement the methods that AbstractFoo doesn't implement and aren't relevant to the test (throw some exception) - this should be very easily generated and maintained by your IDE even if the interface of AbstractFoo changes.

In some cases, we also want to test external libraries. In particular, when a certain Magnolia feature relies on a specific behavior of such library, we might want to assert that it indeed does behave the way we think it does, and that it continues on doing so in their future releases. For such cases, selfTest methods can help, but if we’re testing more than that, then we can envision FooBarLibTest classes (where FooBar is the class under test, and LibTest is the suffix) - we’re “testing the libs of FooBar”.

Resources

Mock Object: http://en.wikipedia.org/wiki/Mock_object

Mockito Documentation: http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

Mockito Examples: http://gojko.net/2009/10/23/mockito-in-six-easy-examples/