StaticAnalysis

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

It's better to always "fail fast" during a build, than to possibly fail during runtime.

 

 

JSFUnit provides a set of unit tests for static analysis of JSF applications:

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Why do we need static analysis?

Some of the problems with configuration or view definitions of a JSF application are either discovered during application startup or during simple rapid UI-tests. But not all of them. If a managed bean is used only in very special usecases and only when some special application state is reached, then the misconfiguration might slip through all QA-phases that you put before production. Static analysis tries to discover misconfiguration during one of the cheapest QA-phases: development and integration.

The three areas checked by static analysis are:

  • application configuration
  • view definitions
  • TLD files

 

Application configuration

JSF version 1 only knows xml-file based application configuration. JSF 2 adds annotation based configuration. Currently static analysis supports only file based configuration. It tries to answer questions like:

  • Do all session and application scoped managed beans implement the java.io.Serializable interface?
  • Do the managed beans implement setter and getter accessors for alll configured attributes?
  • Do the configured classes implement the specified interfaces or extend the correct framework classes (eg. Converter classes must implement the javax.faces.convert.Converter interface)?

Usage, Test creation

 

View definitions

Static analysis tries to answer questions like:

  • Do the view definitions (facelet xhtml files or JSP's) contain EL-expressions that refer to undefined managed  beans?
  • Do the reffered to managed beans implement all necessary setter and getter accessors?

Usage, Test creation

 

TLD files

Static analysis tries to answer questions like:

  • Are the tag names unique?
  • Do the tag handlers implment/extend the correct framework classes?
  • Are the tag-attributes correctly configured?

Usage, Test creation

 


Testing the application configuration

 

In your own TestSuite (e.g. the AllTests testsuite) you can ask org.jboss.jsfunit.analysis.ConfigFilesTestSuite to add the TestCases for your config files. ConfigFilesTestSuite just needs a List of path-entries to your config files and the jar-files containing the configured classes in the classpath of the JUnit-test.

public class AllTests extends TestSuite
{
   public static Test suite()
   {
      TestSuite suite = new TestSuite();
      suite.setName("MyApplication all tests");
 
      List<String> configFiles = new ArrayList<String>();
      configFiles.add("relative/path/to/my/jsf-config-file.xml");
      configFiles.add("relative/path/to/my/another_jsf-config-file.xml");
 
      suite.addTest(new ConfigFilesTestSuite("JSF config files").getSuite(configFiles));
      //add other tests as usual
 
      return suite;
   }
}

Attention: This code generates a hierarchical construct of suites and cases from the passed in config files and executes that suite. As a certain amount of XML-parsing is involved, the suite-construction can take its time. One case of a huge real world application with 7 config files generating more than 500 test-cases, takes up to 45 seconds to initialize. For this reason a few lines are written to System.out to document this setup phase.

 

Dependencies

Apart from the application jar-files and classes the following dependencies should be resolvable through the classpath while running the test:

  • JSF api (maven: javax.faces : jsf-api)
  • EL-api (maven: javax.el : el-api)

References to configuration items from third-party libraries are resolved from the classpath jar files. As of version 1.3.0 the classpath jar-files will be scanned for META-INF/faces-config.xml files and those will be used to resolve missing configuration items. A tipical use case for this is a renderkit (eg. Trinidad) configured as the default render kit (see https://jira.jboss.org/browse/JSFUNIT-251).

 

Change history

  • JSFUNIT-139: to resolve this issue, this part of static analysis has been refactored. org.jboss.jsfunit.analysis.AbstractFacesConfigTestCase has been replaced by org.jboss.jsfunit.analysis.ConfigFilesTestSuite and dependant TestSuites and TestCases.

 

Jar-Files

Currently StaticAnalysis is not included in the JSFUnit build. Test-jars (jboss-jsfunit-analysis-1.2.0.GA-SNAPSHOT.jar) are attached to this page.

As soon as other testers confirm that they work, we can start to include StaticAnalysis again into JSFUnit.

 

Top

 


Extending org.jboss.jsfunit.analysis.AbstractViewTestCase

 

public class ViewTestCase extends org.jboss.jsfunit.analysis.AbstractViewTestCase {

     private static Set absoluteViewPaths = new HashSet<String>() {{
          add("C:/work/project/src/home.xhtml");
     }};
     
     private static Set recursiveViewPaths = new HashSet<String>() {{
          add("C:/work/project/src/views");
     }};
     
     public MyViewTestCase() {

          super(absoluteViewPaths, recursiveViewPaths, 
                    "C:/work/project/src/faces-config.xml");
     }

}

 

 

Do any of your facelets templates or well formed JSPs reference nonexistent managed beans?

 

<h:commandButton value="Home" action="#{missingBean.myAction}" ></h:commandButton>

%%(color:red)junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml has an EL expression '{missingBean.myAction}' which references a managed bean 'missingBean', but no managed bean by this name can be found.%%

 

<h:commandButton value="Home" actionListener="#{missingBean.myActionListener}" ></h:commandButton>

junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml has an EL expression '{missingBean.myActionListener}' which references a managed bean 'missingBean', but no managed bean by this name can be found.

 

<h:commandButton value="Home">
     <f:actionListener binding="#{missingBean.myOtherActionListener}" ></f:actionListener>
</h:commandButton>          

junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml has an EL expression '{missingBean.myOtherActionListener}' which references a managed bean 'missingBean', but no managed bean by this name can be found.

 

 

Do any of your templates or JSPs have EL expressions for nonexistent managed bean actions or action listeners?

 

<h:commandButton value="Home" action="#{managedBean.myAction}" ></h:commandButton>

)junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml contains EL {managedBean.myAction}, but managed bean managedBean->org.jboss.jsfunit.demo.ManagedBean does not have a myAction method.

 

<h:commandButton value="Home" actionListener="#{managedBean.myActionListener}" ></h:commandButton>

junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml contains EL {managedBean.myActionListener}, but managed bean managedBean->org.jboss.jsfunit.demo.ManagedBean does not have a myActionListener method.

 

<h:commandButton value="Home">
     <f:actionListener binding="#{managedBean.myOtherMissingActionListener}" ></f:actionListener>
</h:commandButton>

(color:red)junit.framework.AssertionFailedError: C:/work/workspace/jsf-unit/src/views/home.xhtml contains EL {managedBean.myOtherMissingActionListener}, but managed bean managedBean->org.jboss.jsfunit.demo.ManagedBean does not have a myOtherMissingActionListener method.

 

 

 


Extending org.jboss.jsfunit.analysis.AbstractTldTestCase

 

   public class TldTestCase extends AbstractTldTestCase {

     private static Set<String> paths = new HashSet() {{
          add("C:/work/workspace/jsf-unit/src/demo.tld");
     }};
     
     public TldTestCase() {
          super(paths);
     }
   }

 

Correct Tag Attribute Types?

    <tag>
        <name>checkoutTag</name>
        <tag-class>com.foo.demo.CheckoutTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>itemCount</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.Integer</type>
        </attribute>
    </tag>

junit.framework.AssertionFailedError: Tag 'checkoutTag' in TLD 'demo'

is a javax.faces.webapp.UIComponentTag. Becuase it is a JSF 1.1 tag,

each tag attribute must be of type java.lang.String, however attribute 'itemCount'

is of type java.lang.Integer. See JSF Spec 1.2 section 9.3.1.1 for more information.

Unique Tag Names?

    <tag>
        <name>checkoutTag</name>
        <tag-class>com.foo.demo.CheckoutTag</tag-class>
        <body-content>empty</body-content>
    </tag>

    <tag>
        <name>checkoutTag</name>
        <tag-class>com.foo.demo.duplicate.CheckoutTag</tag-class>
        <body-content>empty</body-content>
    </tag>

junit.framework.AssertionFailedError: Tag 'checkoutTag' occurs

in tag library 'demo' more than once  

Correct Tag Inheritance?

    <tag>
        <name>checkoutTag</name>
        <!-- Just a POJO -->
        <tag-class>com.foo.demo.CheckoutTag</tag-class>
        <body-content>empty</body-content>
    </tag>

junit.framework.AssertionFailedError: class com.foo.demo.CheckoutTag

configured in demo needs to be a javax.faces.webapp.UIComponentTag or a javax.faces.webapp.UIComponentTagBase

Unique Tag Attributes?

    <tag>
        <name>checkoutTag</name>
        <tag-class>com.foo.demo.CheckoutTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>itemCount</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>itemCount</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>        
    </tag>

junit.framework.AssertionFailedError: Attribute itemCount

in demo:checkoutTag is duplicated.

 

Top

 


 

Dependencies

 

AbstractFacesConfigTestCase has a runtime dependency on the JSF 1.2 API (even if you are using JSF 1.1), such as myfaces-api-1.2.0.jar

 

AbstractTldTestCase has a runtime dependency on maven-taglib-checker, clogging and jsp-api-2.1.jar