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?
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)}.
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.
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.
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.
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
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With