1 Reply Latest reply on Feb 22, 2009 4:30 PM by ssilvert

    Unit Testing Backing Beans with Mock dependencies

    ranganathkini

      I work on a project which uses JSF (MyFaces) + Facelets, Spring Framework, Hibernate. I am writing JUnit test cases for testing the DAO and Service layers implemented as POJOs and integrated via Spring. Accidentally my team has coded a large amount of business logic into the backing beans which now requires us to write unit tests for the backing beans. The Service layer components are plugged into the Backing Beans via Spring's DelegatingVariableResolver configured in my faces-config.xml.


      // Code in setUp()
      JSFSession session = new JSFSession("/login.jsf");
      client = session.getJSFClientSession();
      server = session.getJSFServerSession();
      
      // code in some test method
      server.getFacesContext().getApplication().setVariableResolver(
       new VariableResolver() {
       public Object resolveVariable(FacesContext fcxt, String var) {
       PersonService ps = EasyMock.createStrictMock(PersonService.class);
       EasyMock.expect(ps.authenticate("johndoe", "secretpassword"))
       .andReturn(true).once();
       EasyMock.replay(ps);
       return ps;
       }
       }
      );
      
      client.setValue("loginForm:userName", "johndoe");
      client.setValue("loginForm:password", "johndoe");
      client.click("loginForm:loginButton");
      


      As you can see, I have setup the objects to be mocked, after the initial page request to the login.jsf. Which means that the initialization of the backing bean has taken place. It seems to be working pretty well for now. But I have a scenario where I want to mock a dependency at the time when the backing bean needs to initialize. For example, say in my faces-config.xml I have this:

      <managed-bean>
       <managed-bean-name>myBackingBean</managed-bean-name>
       <managed-bean-class>com.foo.MyBackingBean</managed-bean-class>
       <managed-bean-scope>request</managed-bean-scope>
       <managed-property>
       <property-name>personService</property-name>
       <value>#{personService}</value>
       </managed-property>
       <managed-property>
       <property-name>initialized</property-name>
       <value>true</value>
       </managed-property>
      </managed-bean>


      And here is the backing bean code.

      public class MyBackingBean {
       // the dependency I need to mock
       private PersonService personService;
      
       public MyBackingBean() {
       }
      
       public void setPersonService(PersonService ps) {
       personService = ps;
       }
      
       public void setInitialized() {
       // call to the dependency
       ps.doSomeImportantInitialization();
       }
      
      
       // other methods of the backing beans
      
      }


      I want to be able to mock PersonService here, i.e. the one which the backing bean will call at the time of initialization. How can I do this as I have access to the FacesContext via JSFServerSession only after the request is made. How can I do this with JSFUnit?


        • 1. Re: Unit Testing Backing Beans with Mock dependencies
          ssilvert

          This is an interesting question that goes to the heart of how JSF applications should be tested. I hope others will join the discussion.

          I generally advise against using Mocks if at all possible. I'm solidly in the "assert state, not behavior" camp. This just means I prefer stubs over mocks. See http://martinfowler.com/articles/mocksArentStubs.html.

          But what I really prefer is to use neither mocks nor stubs and use real objects instead. In the article, Fowler mentions that real objects are indeed preferred and it is that idea that inspired me to create JSFUnit. So with JSFUnit, you are running in-container with access to all the real objects with real implementations. You theoretically don't need mocks and stubs, though you might want to use some sort of stubbed-out database.

          So now you are probably asking yourself, "Then how do I test the business logic in my backing beans?" The most common way is to assert the state of the system after the request. So you make the request to the login page as you did above. Then ask yourself, "What do I expect the system to look like when the request is done?" Perhaps the answer is that there is some token left in the HttpSession after successful login. So you just use the JSFServerSession to assert that the token is indeed present. Or maybe some record is created in the database. So you check for that.

          The point is that in favoring assertion of state instead of behavior, you don't really care if a certain method was called on the backing bean and you don't care what it returned. You just care if the test left the system in the desired state.

          If you want your test to be more fine-grained then you can get a reference to the backing bean and call its methods directly. The nice thing about doing that with JSFUnit is that you never have to set up mocks and stubs. You test the real backing bean.

          JSFSession jsfSession = new JSFSession("/index.jsf");
          JSFServerSession server = jsfSession.getJSFServerSession();
          MyBean myBean = (MyBean)server.getManagedBeanValue("#{mybean}");
          assertEquals("expected value", myBean.myMethod());

          I think this is much easier and also makes for a more valid test because you don't have to worry about erroneous mocks and stubs.


          As a side note. I'm not sure if you are aware, but your code is illegal according to the javadoc for Application.setVariableResolver():
          It is illegal to call this method after the application has received any requests from the client. If an attempt is made to register a listener after that time it must have no effect.

          // code in some test method
          server.getFacesContext().getApplication().setVariableResolver(
           new VariableResolver() {
           public Object resolveVariable(FacesContext fcxt, String var) {
           PersonService ps = EasyMock.createStrictMock(PersonService.class);
           EasyMock.expect(ps.authenticate("johndoe", "secretpassword"))
           .andReturn(true).once();
           EasyMock.replay(ps);
           return ps;
           }
           }
          );


          The fact that it worked appears to be a bug in your JSF implementation. If you want to do this in a spec-compliant way then you should create your VariableResolver in a separate class and add it in a faces-config.xml file to be instantiated when the app is loaded.

          Stan