Tuesday 28 September 2010

MyFaces Test framework

Introduction

I saw a lot of projects where testing could be better and altough a
lot of people won't believe me, good unit testing of your code reduces the
bugs found during functional testing and allows for easier maintenance
later on. But even if the developers are writing a lot of unit tests, some
cases can't be handled properly. Within JSF, you can work a lot with
POJO's which are independent of all other stuff but there are cases where
a kind of integration test could be handy.


Another framework?

And by an integration test, I don't mean that you should have a
webserver started and some testing done in-container. However, on this
area thare are lately a few very promising initiatives that a lot of you
will hear of during the next year or so.

Let me give an example of what I mean by developping some percentage
support within JSF. The value will be stored in the bacing bean (and thus
probably also like that in the persistence store like a database) as a
value between 0 and 100, both values included. On the screen however, we
would like to show it with a percentage sign after the value like
15%.

To make the case a little bit more interesting, we allow also the
fractional format as input so 0.15 will also be considered as 15% and thus
we want 15 in the backing value. This list contains some examples:


  1. 15% -> 15
  2. 0.15% -> 0.15
  3. 0.15 -> 15
  4. test -> error
  5. 15 -> 15 (we encourage the lazy user by allowing him to ommit the final %)
  6. -15% -> error

The code for the converter (PercentageConverter) and the validator
(PercentageValidator) can be found in the example. Why do we need a
validator? Well the converter should only handle the convertions. When the
HTML is built up, it needs to convert the Double value 15 to the String
15%. During the decoding, the apply request value phase, it converts the
entered text, if it is possible, to a Double value. So for the 6 mentioned
examples above, we have as a result of the getAsObject method a Double as
method result except for the 4th case where we return just the entered
String. A validator is a better place to make sure we have entered a
correct percentage value.

So we have 2 JSF artefacts that combined, gives us the required
functionality. And although we can write some unit testing for those
classes, we could imagine that some integration testing gives us more
confidence that it actually will work (and in more complex cases, you sure
need such thing).

Such integration testing can be done now very easily when the just
released version 1.0.0 of MyFaces Test. It is not brand new code, it is
based on the Apache Shale Test. Since the Apacha Shale code is no longer
maintained, the valuable Test module is taken over by the MyFaces group.
Besides the refactoring in different package names, there are a few
additional features added. The 2 most important ones are the new module
for JSF 2.0 and the migration to JUnit 4.

What is MyFaces test? The main part of the project is a set of mock
objects for the JSF Context. Thay way, we can execute large parts of the
JSF lifecycle outside a container. And thus we can make some integration
testing like for the example we have described here. This is an extract of
the test class that makes such tests:

public class TestPercentage extends AbstractTest {

    private TestBean bean;

    @Test
    public void testDecode1() throws IOException, SAXException {
        // Create required beans and dependency mocks
        bean = new TestBean();
        facesContext.getExternalContext().getRequestMap().put("testBean", bean);
        
        // Value/Method binding of JSF components
        createValueBinding(inputComponent, "value", "#{testBean.percentage}");

        // Config JSF Components (normaly done in view like .xhtml)
        inputComponent.setConverter(new PercentageConverter());
        inputComponent.addValidator(new PercentageValidator());

        // User inputs some value
        inputComponent.setSubmittedValue("15%");

        // JSF Lifecycle
        inputComponent.validate(facesContext);
        inputComponent.updateModel(facesContext);

        // Correct value in the backing bean?
        Assert.assertEquals(15, bean.getPercentage());

    }
}

The contents of the AbstractTest class will be discussed later on,
for now you just need to know that it prepares you the mock environment
and some objects that you can use in your tests like a inputComponent. At
first the test method seems to contain a lot code, but if you know a bit
the internals of JSF, they all seem logic.

First we define a bean and expose it to the managed bean facility of
JSF in the request scope. Important here is the name which we use define
it, in this test it is 'testBean'. Then we create the connection between
the UIComponent and the backing bean by assigning the correct EL to the
value attribute of it. Here is the 'testBean' important and must be equals
to the defined bean name. The percentage is trhe name of the property in
the beacking bean, here in the testBean class.

Then we assign the correct converter and validtor to the component.
Here for our tests very important since it is just those JSGF artefacts we
like to test. In the next step we assign some user input to the component.
By setting a string to the submitted value, we mimic the behaviour of the
apply resquest values phase of JSF. The we go on by calling te next 2
phases of theJSF life cycle, the validation and the Update model
phase.

In this stage we have in our backing bean the value we should have,
if the code in our converter and validator is behaving as we designed. So
a simple test of the value of the backing bean property can tell us
that.

With this example we can test the first scenario of the list above.
Other test methods can be written in the same way to test the other
acceptance criteria of our components. And although it seemed complicated
at first, we have a powerful tool that can be used out side of a container
to make some tests that are very tight integrated to the JSF
lifecycle.

Besides the test methods for the other scenarios, you can find in
the attached code also the test method to test the decoding capabilities
of uor converter. The main command here is the usage of the

Then we need to say a few words about the AbstractTest class we are
extending from. The class itself is an extension of
AbstractJsfConfigurableMockTestCase, the class that does all the hard work
for us. For our intro here, we have a look at the 2 most important
methods, setup and setUpJSFObjects.

@Before
    public void setUp() throws Exception {

        // Set up a new thread context class loader
        threadContextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0],
                this.getClass().getClassLoader()));

        // Set up Servlet API Objects
        setUpServletObjects();

        // Set up JSF API Objects
        FactoryFinder.releaseFactories();

        setFactories();

        setUpJSFObjects();
    }
    
    protected void setUpJSFObjects()  throws Exception
    {
        setUpExternalContext();
        setUpLifecycle();
        setUpFacesContext();
        setUpView();
        setUpApplication();
        setUpRenderKit();
    }

As the comment says, the setUpServletObjects creates mocks
for all the web related concepts like servlet context, Http session,
servlet request and servlet response. Then all the factories for JSF are
specified and then each JSF concept created, with mocks.

The good thing is that all methods, like setUpFacesContext, are all
protected methods that we can override in our extensions. 3 of these
methods are overwritten to add some functionality.

The setUpFacesContext is used to set in place a response writer that
sends the generated HTML to a StringWriter that we can check to validate
some converters (as in our example above).

The setUpView method is extended to create a form with 3 components,
a UICompoent, a SelectOneCompoent and a commandButton. If you ever write
some tests with the MyFaces Test Framework, that same method can be use to
instantiate the needed components for your test.

The last method that we hijacked is the setUpRenderKit, to add the
default faces config to our complete mock system. See the code how it is
done and be surprised that it can be achived in 2 lines of code. This is
required if we want to do some tests where we are using the decoding
phase, as we need the renderers that are available.

The code for the percentage validation can be found here

Conclusion

As indicated, the MyFaces test framework is a very valuable add-on
to the battery of options we have to tests our code. It allows to write
out of container tests, which are thus very rapid and easy to setup. And
in the mean time allows us to create some tests which where previously not
feasible.

No comments:

Post a Comment