# Step 7 - Finish the job properly

JuZcret application is now finish but our project is not perfect…
All through the JuZcret tutorial we ignore to tell you about Unit Test and this for sure, is not a good practice.
The reason is that we wanted to keep you focus on a specific topic during each step. It’s why it’s only during this last step that we’ll talk about Unit Test in Juzu.
The good point is that Juzu allow you to easily leverage selenium for simulating real application while taking advantage of the speed of JUnit.

So it’s time to write Unit Test for our JuZcret portlet. The portlet will be deploy to an embedded portlet container. Selenium and webdriver will help to simulate almost all user interactions with the application, then Arquillian will help to integrate with JUnit.

# Dependencies

So we will use:

- JUnit 4

- Arquillian: A testing framework for managing containers and write integration test

- ShrinkWrap: Arquillian’s little brother for creating Java archives easily

- Selenium WebDriver: A simple API for simulating browser behavior

For making testing easy, Juzu provides a Maven dependencies called depchains containing all needed dependencies for testing an application with those tools: `juzu-depchain-arquillian` and `juzu-depchain-arquillian-tomcat7` that should be already in your `pom.xml`:

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.juzu</groupId>
      <artifactId>juzu-depchain-arquillian</artifactId>
      <version>1.0.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.juzu</groupId>
      <artifactId>juzu-depchain-arquillian-tomcat7</artifactId>
      <version>1.0.0</version>
      <scope>test</scope>
    </dependency>

Also Juzu core provides some abstract test class, that make our test easier to interact with Arquillian. Lets add this dependency and this plugin:

           [...]

           <dependency>
              <groupId>org.juzu</groupId>
              <artifactId>juzu-core</artifactId>
              <version>1.0.0</version>
              <type>test-jar</type>
              <scope>test</scope>
           </dependency>

        [...]

                  <!-- juzu-core test jar need this configuration -->
                  <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.16</version>
                    <configuration>
                    <systemPropertyVariables>
                      <targetDir>${project.build.directory}</targetDir>
                      <juzu.test.compiler>javac</juzu.test.compiler>
                      <juzu.test.resources.path>${basedir}/src/test/resources</juzu.test.resources.path>
                      <juzu.test.workspace.path>
                        ${project.build.directory}/workspace
                      </juzu.test.workspace.path>
                    </systemPropertyVariables>
                    </configuration>
                  </plugin>

            [...]

There are xml parsing libraries conflicts between htmlunit webdriver and our eXo JCR. We’ll need to add some "exclusions" tag into the `pom.xml`. Update the `exo.jcr.component.ext` dependency:

         <dependency>
            <groupId>org.exoplatform.jcr</groupId>
            <artifactId>exo.jcr.component.ext</artifactId>
            <version>1.15.x-SNAPSHOT</version>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                   <groupId>xml-apis</groupId>
                   <artifactId>xml-apis</artifactId>
                </exclusion>
                <exclusion>
                   <groupId>org.exoplatform.core</groupId>
                   <artifactId>exo.core.component.document</artifactId>
                </exclusion>
             </exclusions>
          </dependency>

# Test configuration and Mocks

We need to add a configuration file for arquillian: `src/test/resources/arquillian.xml`

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <arquillian
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://jboss.org/schema/arquillian"
      xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

      <extension qualifier="webdriver">
        <!-- Needed for html unit web driver -->
        <property name="javascriptEnabled">true</property>
      </extension>

      <container qualifier="tomcat" default="true">
        <configuration>
          <property name="bindHttpPort">8080</property>
        </configuration>
      </container>

    </arquillian>

# Overwrite service implementation

We only want to focus to testing the JuZcret controller, but not the JCR service. It’s better to mock the SecretService JCR implementation. The good news is we already have an in-memory service implementation (remember step-2). The bad news is that today we cannot override the service declared in `package-info.java` in the unit test. For now, we have a workaround and for the future, we plan to improve the juzu-core unit test support.

Create a mock for secret service and lets add it into `src/test/java/org/juzu/tutorial/services`:

    package org.juzu.tutorial.services;

    import javax.inject.Singleton;

    import java.util.List;
    import java.util.Set;

    import org.juzu.tutorial.models.Comment;
    import org.juzu.tutorial.models.Secret;

    @Singleton
    public class SecretServiceJCRImpl implements SecretService {

      private SecretService delegate;

      public SecretServiceJCRImpl() {
        this.delegate = new SecretServiceMemImpl();
      }

      @Override
      public List<Secret> getSecrets() {
        return delegate.getSecrets();
      }

      @Override
      public void addSecret(String message, String imageUrl) {
        delegate.addSecret(message, imageUrl);
      }

      @Override
      public Comment addComment(String secretId, Comment comment) {
        return delegate.addComment(secretId, comment);
      }

      @Override
      public Set<String> addLike(String secretId, String userId) {
        return delegate.addLike(secretId, userId);
      }
    }

Classloader of the test will load this service `SecretServiceJCRImpl` instead of the one in main source. This mock service delegate all the task to our in-memory implementation.

| If you are using IntelliJ you can get a "Duplicate class found in the file" warning due to the fact that we have two `SecretServiceJCRImpl` in the same package (one in `/main`, one in `/test`). Just ignore it.

We also have `SessionProviderService` and `NodeHierarchyCreator` which are eXo JCR service in `package-info.java`. We don’t need them for the test but we didn’t declare an implementation for them in `package-info.java`. So we will mock the eXo kernel provider to avoid to get an error when it will try to bind the implementation.

Lets mock the eXo kernel provider in `src/test/java/org/juzu/tutorial`:

    package org.juzu.tutorial;

    import javax.inject.Provider;

    import juzu.inject.ProviderFactory;

    public class MockProviderFactory implements ProviderFactory {

      @Override
      public <T> Provider<? extends T> getProvider(final Class<T> implementationType) throws Exception {
        return new Provider<T>() {
          @Override
          public T get() {
            return null;
          }
        };
      }
    }

Notice that the provider return null instance, it’s just the mock provider to satisfy the IOC container. We don’t need any JCR service instance in the test.

We need also to register the mock to service loader by creating `src/test/resources/META-INF/services/juzu.inject.ProviderFactory`:

    org.juzu.tutorial.MockProviderFactory

# Test cases

We decide to have a dedicated test case for each result of tutorial step. We’ll simulate all available user interaction with the JuZcret portlet using Selenium.

| There still 2 actions that can not simulated for now: changing the language, and the portlet mode. This should be improved in the future version.

We will develop our Unit Test in `JuZcretTestCase.java` file in `src/test/java/org/juzu/tutorial`:

    package org.juzu.tutorial;

    import juzu.test.AbstractWebTestCase;
    import org.jboss.arquillian.container.test.api.Deployment;
    import org.jboss.arquillian.drone.api.annotation.Drone;
    import org.jboss.shrinkwrap.api.spec.WebArchive;
    import org.openqa.selenium.WebDriver;

    public class JuZcretTestCase extends AbstractWebTestCase {

        @Deployment(testable = false)
        public static WebArchive createDeployment() {
            return createPortletDeployment("org.juzu.tutorial");
        }

        @Drone
        WebDriver driver;

    }

We use `createPortletDeployment` method from the abstract test class of juzu-core that allow to deploy our portlet into an embedded portlet container.
WebDriver is injected by arquillian and help to simulate the user interactions.

# Test rendering

After step-1, we have a running portlet, that render the `secretWall.gtmpl`. Unit test should help to make a quick test on the result of render process

    import org.junit.Test;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebElement;

    [...]

      @Test
      public void testRender() throws Exception {
        driver.get(getPortletURL().toString());
        WebElement body = driver.findElement(By.tagName("body"));
        assertTrue(body.getText().indexOf("JuZcret Portlet") != -1);
        System.out.println(driver.getPageSource());
      }

    [...]

Our first test case is very simple:

1. Make the request, get the html body element and be sure that it contains the substring "JuZcret Portlet"

2. Printing out the whole server response to the console to see the result

# Test adding secret

After step-2, user is able to add new secrets. Thanks to arquillian and webdriver, we can easily simulate user input, and submit form in a JUnit test. Lets add this new test case for adding secret:

    import org.openqa.selenium.support.ui.ExpectedCondition;
    import org.openqa.selenium.support.ui.WebDriverWait;

    [...]

      @Test
      public void testSecret() throws Exception {
        driver.get(getPortletURL().toString());
        WebElement body = driver.findElement(By.tagName("body"));
        assertFalse(body.getText().contains("test secret text"));

        // add secret form
        WebElement shareBtn = driver.findElement(By.cssSelector(".secret-wall-heading a"));
        driver.get(shareBtn.getAttribute("href"));
        // input
        WebElement secretInput = driver.findElement(By.tagName("textarea"));
        secretInput.sendKeys("test secret text");
        // submit
        WebElement submitBtn = driver.findElement(By.tagName("button"));
        submitBtn.click();

        // wait for redirecting to index page
        body = new WebDriverWait(driver, 10).until(new ExpectedCondition<WebElement>() {
          public WebElement apply(WebDriver drv) {
            return drv.findElement(By.tagName("body"));
          }
        });
        assertTrue(body.getText().contains("test secret text"));
      }

1. We assert that there is no text "test secret text" in the secret list

2. WebDriver provide API for finding elements in a html page. We find the url for the add secret page

3. Find the textarea, and button, then fill the form, and submit. All are written using java api to simulate the actions. That’s fast and clean way for UI test

4. After submitting the add secret form, the portlet will redirect to home page, notice that it may take some time, so we need to tell WebDriver to wait until we have the response from server by `WebDriverWait`

# Test Assets

We have tested for rendering and user interactions. In step 3 we improved the portlet Look&Feel. So we should test if the portlet is served with correct assets (css, and js files), to make sure all our declaration for assets in `package-info.java` are correct:

    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;

    [...]

    @Test
      public void testAsset() throws Exception {
        driver.get(getPortletURL().toString());

        List<WebElement> scripts = driver.findElements(By.tagName("script"));
        Set<String> srcScripts = new HashSet<String>();
        for (WebElement elem : scripts) {
        srcScripts.add(elem.getAttribute("src"));
        }
        assertTrue(srcScripts.contains("http://localhost:8080/juzu/assets/org/juzu/tutorial/assets/jquery/1.10.2/jquery.js"));
        assertTrue(srcScripts.contains("http://localhost:8080/juzu/assets/juzu/impl/plugin/ajax/script.js"));
        assertTrue(srcScripts.contains("http://localhost:8080/juzu/assets/org/juzu/tutorial/assets/javascripts/secret.js"));

        WebElement style = driver.findElement(By.tagName("link"));
        assertEquals("http://localhost:8080/juzu/assets/org/juzu/tutorial/assets/styles/juzcret.css",
                       style.getAttribute("href"));
      }

All necessary assets should be in the server response for rendering JuZcret. This test allow to check that all are presents:

Our portlet need 3 javascript files:

- scripts.js: This file is juzu-core ajax script, it provides jquery plugin to make ajax request to our juzu controller method

- jquery.js: JQuery is used by script.js and our portlet js

- secret.js: Our application js file

The juzcret.less should be compiled and served as juzcret.css.

# Test Ajax actions

In step-5 we add some user interactions that was done by using Ajax. Fortunately, HtmlUnit do a well job on simulating browser. It can execute javascript, even ajax action.

| Remember that we have enable js in `arquillian.xml`: `<property name="javascriptEnabled">true</property>`

Lets test the like feature:

    import org.openqa.selenium.support.ui.ExpectedConditions;

    [...]

    @Test
      public void testLike() throws Exception {
        driver.get(getPortletURL().toString());

        // like
        WebElement likeBtn = driver.findElement(By.cssSelector(".btn-like"));
        likeBtn.click();

        // wait
        By selector = By.cssSelector(".btn-like .numb");
        ExpectedCondition<Boolean> condition = ExpectedConditions.textToBePresentInElement(selector, "1");
        assertTrue(new WebDriverWait(driver, 10).until(condition));
      }

The test is pretty simple:

1. Requesting the index page, click the like button

2. Don’t forget to wait until we have server response, the timeout is 10 second

The last test, the comment feature test case:

      @Test
      public void testComment() throws Exception {
        driver.get(getPortletURL().toString());
        WebElement body = driver.findElement(By.tagName("body"));
        assertFalse(body.getText().contains("test comment"));

        // input
        WebElement commentInput = driver.findElement(By.cssSelector(".secret-add-comment"));
        commentInput.sendKeys("test comment");
        // submit
        WebElement submitBtn = driver.findElement(By.cssSelector(".btn-comment"));
        submitBtn.click();
        // wait
        ExpectedCondition<Boolean> condition = ExpectedConditions.textToBePresentInElement(By.cssSelector(".secr-comments-list"),
                                                                                           "test comment");
        assertTrue(new WebDriverWait(driver, 10).until(condition));
      }

1. Check that no comment with the substring "test comment" already exist

2. Add a new comment with the message "test comment"

3. Click on the button to submit the new comment

4. Don’t forget to wait until we have server response, the timeout is 10 second

Now our JuZcret application is complete.

Perform a

    $ mvn clean install

and ensure that all tests success.

This step is the end of the JuZcret tutorial.
Apprentice, you can be proud. You are now a true Juzu developer with the capability to develop more and more funny Juzu applications and evangelize Juzu around you.

If you have any questions, jump to the Juzu forum, we will be pleased to help you.

If you want to contribute to Juzu, here is the Github repo and don’t hesitate to contact us.