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