Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test JSF bean methods that use session parameters?

I'm having a hard time trying to implement unit tests on my JSF backing bean classes... For instance some of the methods use session or request parameters, obtained using this sort of code:

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("paramKey");.

My question is: how can I test a method which obtains values from the session or request?

like image 555
Felipe Reis Avatar asked Jul 28 '13 17:07

Felipe Reis


People also ask

How to pass parameter value from JSF page to backing bean?

As i know,there are 4 ways to pass a parameter value from JSF page to backing bean : Method expression (JSF 2.0) Let see example one by one : 1. Method expression Since JSF 2.0, you are allow to pass parameter value in the method expression like this # {bean.method (param)}.

How do you validate a bean in JSF?

JSF Bean Validation. JSF provides validation constraints for bean model in the form of annotations. You can place that annotations on a field, method, or class of a JavaBeans component, such as a managed bean. JSF also provides facility to create custom or user defined constraints.

How many session beans should a JSF application have?

It is totally vital to have a session scoped managed bean in your JSF application that stores information needed throughout the user session, but it is good practice to have only one session bean. If you feel you need more, think about the basic design of your application.

What is @JSF annotations in JavaBeans?

JSF provides validation constraints for bean model in the form of annotations. You can place that annotations on a field, method, or class of a JavaBeans component, such as a managed bean.


2 Answers

What I usually do is to avoid calling static methods into the beans I want to test. That implies your current code to be refactored:

FacesContext.getCurrentInstance().getExternalContext()
    .getSessionMap().get("paramKey");

Are there ways to test them with their static method calls? Probably there are, but they led me to a lot of trouble more than help. So at the end I got rid of them and changed my design. Just let a second bean do it (which you'll mock later). In your case, create a @SessionScoped bean which manages that functionality:

@ManagedBean
@SessionScoped
public class SessionBean{

    public Object getSessionParam(String paramKey){
        FacesContext.getCurrentInstance().getExternalContext()
           .getSessionMap().get(paramKey);
    }

}

And inject that bean in every single bean that needs it (I usually extend my view/request beans from an abstract bean which has it, so don't have to implement it in every single bean):

@ManagedBean
@RequestScoped
public class RequestBean{

    @ManagedProperty(value="#{sessionBean}")
    private SessionBean sessionBean;

    public void accessSessionParam(){
        sessionBean.getSessionParam("name");
    }

}

That way you can access static methods easily, via your auxiliary SessionBean. Then, how to test it? Just create a mock of it (using Mockito, for instance):

public class Test{

    public void test1(){
        SessionBean sb = Mockito.mock(SessionBean.class);
        //Mock your 'getSessionParam' method
        ValueBean vb = new ValueBean();
        Mockito.when(sb.getSessionParam(Mockito.anyString()).thenReturn(vb);
        //Create your bean to test and set your mock to it
        RequestBean rb = new RequestBean();
        rb.setSessionBean(sb);
        //here you can test your RequestBean assuming 
        //sessionBean.getSessionParam() 
        //will return vb for every single call
    }

}
like image 121
Xtreme Biker Avatar answered Nov 16 '22 16:11

Xtreme Biker


It is possible to mock FacesContext but this is less than ideal. Mockito example:

import javax.faces.context.FacesContext;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public abstract class ContextMocker extends FacesContext {
  private ContextMocker() {}

  private static final Release RELEASE = new Release();

  private static class Release implements Answer<Void> {
    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
      setCurrentInstance(null);
      return null;
    }
  }

  public static FacesContext mockFacesContext() {
    FacesContext context = Mockito.mock(FacesContext.class);
    setCurrentInstance(context);
    Mockito.doAnswer(RELEASE)
        .when(context)
        .release();
    return context;
  }
}

If your platform supports it, prefer CDI to JSF managed beans. CDI has static dependency checking and injects proxies to prevent scope leak. CDI doesn't support all of JSF's features but it is relatively easy to connect JSF managed beans to CDI where necessary.

JSF managed beans restrict the scope that you inject types into but even that can lead to scope leaks. For example, the #{sessionScope} variable can be injected into a session scope bean even though the object belongs to the request scoped ExternalContext. It is possible to overcome this by writing your own JSF bean proxies.

Note: most of this was written with Java EE 6 in mind; things might have improved with Java EE 7.

like image 36
McDowell Avatar answered Nov 16 '22 15:11

McDowell