View Unit Testing

Last modified by Manuel Leduc on 2023/03/06 10:22

Concept

This is about writing "unit" tests for code generated by the server-side for the "View" of the MVC model. Namely, it focuses on testing:

  • wiki pages (*.xml files)
  • velocity templates (*.vm files)

Strategy

Specifically the idea is to be able to write integration tests that use the real code except for all the environment parts (database queries, filesystem files, remote server calls, etc) which are mocked in the test. Whereas functional tests fully test everything in a running XWiki instance, View unit tests don't test the interactions with the environment, nor the execution in the browser (JS, CSS). On the other hand, View unit tests execute at a fraction of the cost of functional tests (3mn for a single functional test, < 1s for a View unit test, a few ms for a pure unit test in isolation).

The strategy is to write some functional tests since they are the only tests proving that a features works but to use View unit tests wherever possible to test the different paths of view resources (specifically the different paths taken by the scripts that execute inside of the view resources).

Page Unit Testing

The way those tests work is that the XML file representing the wiki pages are loaded from the filesystem into XWikiDocument instances and a stubbed environment is defined so that XWikiDocument can then be rendered in the desired syntax.


To write such a test starting with the followng steps (common to both Page Unit Testing and Template Unit Testing):

  • Make your POM depend on the org.xwiki.platform:xwiki-platform-test-page module:
    <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-test-page</artifactId>
     <version>${project.version}</version>
     <scope>test</scope>
    </dependency>
  • Write a Java test class that extends PageTest
  • Possibly add some extra component registration that you need through existing annotations (*ComponentList annotations) or through the custom ComponentList annotation. Note that when extending PageTest this automatically brings some base components registration (the list defined in PageComponentList and ReferenceComponentList). Example of other existing annotations:
    • XWikiSyntax20ComponentList: XWiki Syntax 2.0-related components
    • XWikiSyntax21ComponentList: XWiki Syntax 2.1-related components
    • XHTML10ComponentList: XHTML 1.0-related components

Then:

  • You then verify the rendering of a page by setting the output syntax you wish to have, the query parameters and then call renderPage(). For example:
    setOutputSyntax(Syntax.XHTML_1_0);
    request.put("section", "Links");
    request.put("xpage", "print");
    ...
    String result = renderPage(new DocumentReference("xwiki", "XWiki", "XWikiSyntax"));
    assertTrue("...explanation if test fails", result.contains("...content that we wish to verify..."));
  • If you need to have some other pages loaded from sources too (for example if the page you're rendering contains an include macro that loads another page), you'll use the

    loadPage() API as in:loadPage(new DocumentReference("xwiki", "XWiki", "XWikiSyntaxLinks"));

Here's a full example:

@XWikiSyntax20ComponentList
@XHTML10ComponentList
public class WebRssTest extends PageTest
{
   private ScriptQuery query;

   @Before
   public void setUp() throws Exception
   {
        setOutputSyntax(Syntax.PLAIN_1_0);
        request.put("outputSyntax", "plain");
        request.put("xpage", "plain");

        QueryManagerScriptService qmss = mock(QueryManagerScriptService.class);
        oldcore.getMocker().registerComponent(ScriptService.class, "query", qmss);
        query = mock(ScriptQuery.class);
        when(qmss.xwql("where 1=1 order by doc.date desc")).thenReturn(query);
   }

   @Test
   public void webRssFiltersHiddenDocuments() throws Exception
   {
       // Render the page to test
       renderPage(new DocumentReference("xwiki", "Main", "WebRss"));

       // This is the real test!!
       // We want to verify that the hidden document filter is called when executing the XWQL
       // query to get the list of modified pages
       verify(query).addFilter("hidden/document");
   }

   @Test
   public void webRssDisplay() throws Exception
   {
        when(query.addFilter(anyString())).thenReturn(query);
        when(query.setLimit(20)).thenReturn(query);
        when(query.setOffset(0)).thenReturn(query);
        when(query.execute()).thenReturn(Arrays.<Object>asList("Space1.Page1", "Space2.Page2"));

        FeedPlugin plugin = new FeedPlugin("feed", FeedPlugin.class.getName(), context);
        FeedPluginApi pluginApi = new FeedPluginApi(plugin, context);
        when(xwiki.getPluginApi("feed", context)).thenReturn(pluginApi);

       // Render the page to test
       String xml = renderPage(new DocumentReference("xwiki", "Main", "WebRss"));

        assertTrue(xml.contains("<title>activity.rss.feed.description</title>"));
        assertTrue(xml.contains("<title>Page1</title>"));
        assertTrue(xml.contains("<title>Page2</title>"));
   }
}

Template Unit Testing

To write such a test follow the common instruction.

Then:

  • Add the dependencies on the JAR modules containing the templates needed. For example if you need to load directly or indirectly some template located in the xwiki-platform-web-templates maven module (note that macros.vm is loaded automatically by VelocityManager), you'd write:
    <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-web-templates</artifactId>
     <version>${project.version}</version>
     <scope>test</scope>
    </dependency>
  • You then use the TemplateManager to call the template to test. For example:
    public class MentionTemplateTest extends PageTest
    {
       @Test
       void renderTemplateWhenXXX() throws Exception
       {
           // Add any mock for the environment here.

            TemplateManager templateManager = oldcore.getMocker().getInstance(TemplateManager.class);
            String result = templateManager.render("mentions/mention.vm");

           // Perform assertions here on the result.
       }

XWiki 14.2+ 

Loading a Wiki Macro

Loading a Java macro is straightforward and only requires to add the right components in the component list.
Loading a wiki macro requires a few more steps that are defined below.

  1. Add the following dependency to your module
    <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-rendering-wikimacro-store</artifactId>
     <version>${project.version}</version>
     <scope>test</scope>
     <type>test-jar</type>
    </dependency>
  2. Annotate your page test class with org.xwiki.rendering.wikimacro.internal.WikiMacroFactoryComponentClass
  3. Load the wiki macro page using org.xwiki.test.page.WikiMacroSetup#loadWikiMacro

Tags:
   

Get Connected