This wiki page contains the status of the „Automated webapp tests for MyFaces core and extensions” GsoC 2010 Project. Feel free to contribute with comments, issues and improvements.

Description

For now, the source code of the project can be found in a google code project:

and it will soon be available in MyFaces codebase. The API proposal followed, with small changes can be found at:

The scope of this project is to provide a complete and easy to use testing framework for MyFaces core and extensions, but for any webapp user aplication as well. Using this API you will test the real instances and resources of the webapp, assuring both unit and integration testing.

A typical usage of the testing API looks like this:

@View(id = "test.xhtml", pageBeans = @PageBean(clazz = ExampleBean.class)) // this view will be accessed and the beans will be installed
@Configuration(SomeConfiguration.class) // the configuration class contains the Dependencies
@RunWith(WebappTestRunner.class)
public class ExampleTestCase
{
    
    @Assertable
    private ExampleBean exampleBean; // specified as PageBean, thus it is a valid resource, a CGLIB proxy will be injected

    @Tester
    private WebappTester tester; // will provide all necessary methods to control the test case
    
    public void testApplyInputValue()
    {
        webappTestCase.assertThat(exampleBean.getInput()).is("default value").before(PhaseId.RENDER_RESPONSE);
        webappTestCase.input("test value").into("testForm:fieldId");
        webappTestCase.click("testForm:buttonId");

        webappTestCase.assertThat(webappTestCase.getFacesContext().getExternalContext()).is("String");
        webappTestCase.assertThat(exampleBean.getInput()).is("test value").before(PhaseId.INVOKE_APPLICATION);
        webappTestCase.assertThat("#{exampleBean.input}").is("test value").after(PhaseId.INVOKE_APPLICATION);
        webappTestCase.expectCall(exampleBean.action()).in(PhaseId.INVOKE_APPLICATION);
        exampleBean.actionListener(null);
        webappTestCase.expectLastCall().in(PhaseId.INVOKE_APPLICATION);
    }
    
}

For configuring the test case, a set of annotations are provided:

  • Class annotations: @View, @PageBean, @Configuration and @ConfigurationTestSuite
  • Field annotations: @Tester, @Assertable

Alongside this config annotations a testing method will nead @RunWith, @Test and @Deployment annotations provided by JUnit and Arquillian. A short description of each annotation folows:

  • @View: contains an id value for the page view id under test and an pageBeans list for configuring the beans of the page.
  • @PageBean: allows the user to specify the class, scope and name of a page bean within a „@View” config.
  • @Configuration: holds a WebappConfiguration value where you can specify any resource and library that should be added to the test at deploy time.
  • @ConfigurationTestSuite: this annotation can be used to specify a list of configurations(WebappConfiguration type). The API will run the annotated testing class with each of the configuration from the list. An N-Configuration list will generate N tests with specific deploys.
  • @Tester: this field annotation should be used to configure the Tester of the test case. Using this annotation, the API will inject a WebappTester instance at runtime.
  • @Assertable: should be attached to any field that will be the subject of an assertion in the testing method. The API injects a proxy instance for the fields.
  • @Deployment: is the method annotation provided by Arquillian, that configures the deployment archive for testing. This is a drawback of the current version of the API, for now this method should call an Tester.createArchive(); that will create the deployment archive.
  • @Test: JUnit annotation for the actual testing methods.
  • @RunWith: The testing class must contain an „@RunWith(WebappTestRunner.class)” configuration, to tell JUnit to use the API’s runner.

For controlling the test case, the functionality and all the necessary methods are provided using the WebappTester field. This field should be annotated with „@Tester”. By default the API will inject an WebbapTesterImpl instance. With the help of this Tester one can make assertions using:

  • assertThat(methodCall/ELexpression).is(Object).before/after(PhaseId)
  • expectCall(methodCall/ELexpression).in(PhaseId)
  • expectLastCall()

and do actions using:

  • click(buttonId)
  • input(string).into(fieldId)

The Tester offers access to the facesContext and the viewRoot by the „getFacesContext” and „getRoot” methods.

Adding extra functionality and extending the API is very easy. The main access point where a default implementation can be customized and overridden is by replacing the WebappTestConfiguratorImpl default implementation from the services support, with the default config.

In the implementation section of the framework a metadata based solution was used. When running a test, the API just collects informations about the data under test, creating a list of assertions and actions. When the data gathering is finished, the API runs the actual test, launching an initial view request and evaluating each assertion and action.

The main technology behind the API is Arquillian which, with the help of a unit testing framework(JUnit) and ShrinkWrap, can run test cases within a container using the real resource instances. The API is using an embedded Tomcat server as the default container where the test cases will be executed, and the unit testing framework used for testing is JUnit.

The technology behind accessing pages programatically is „HtmlUnit”. With it you can be able to invoke pages, fill out forms, click links, etc. CGLIB was also heavily used for proxy-ing and managing classes by reflection.

Improvements

For now, the unit testing framework supported is JUnit. The API should be extended to add functionality and support for TestNG as well.

The current initial version of the API adds some restraints when using assertions. If a bean instance call is used as an argument for „assetThat()”, then for the given bean only one method call is supported. Ex: „exampleBean.getInput()”-SUPPORTED; „exampleBean.getProperties.getInput()” – NOT SUPPORTED

Improvements should be done for exceptions, messages and logs which are now just shallow implemented.

Weaknesses

The @Deployment annotation, that should be added in any test case is e weakness of the current version. Arquillian is looking for a method configured with this annotation, and if it is not provided it trows an exception. A workaround for this issue will be required for next versions.

For now the Tomcat container support that Arquillian provides requires a servlet-mapping for the WebappServletTestRunner, in the "web.xml" file.

Using a metadata based solution by initially creating a list of test data and doing the actual test afterward, debugging in the test class method will be disabled.

Problems

Feel free to add any problems and issues that you have encountered using the API.

Suggestions

Any feedback and suggestions are welcomed.

  • No labels