2010-11-23 4 views
13

J'essaie d'ajouter des tests unitaires à une application JSF. Cette application na pas compter en grande partie sur les meilleures pratiques, tant de méthodes de service utilisent le FacesContext pour extraire des données à partir de fèves de session gérés comme ceci:Mocking FacesContext

(ce qui est dans une classe util)

public static Object getPageBean(String beanReference) { 
     FacesContext fc = FacesContext.getCurrentInstance(); 
     VariableResolver vr = fc.getApplication().getVariableResolver(); 
     return vr.resolveVariable(fc, beanReference); 
    } 

Quelle serait la meilleure façon de se moquer de cela? J'utilise groovy donc j'ai quelques options plus pour créer des classes que je ne peux pas normalement créer.

Répondre

2

dans mon cas, je pouvais me moquer de lui dans pure groovy. i fournir une carte de MockBeans qu'il peut retourner:

private FacesContext getMockFacesContext(def map){ 
     def fc = [ 
      "getApplication": { 
      return ["getVariableResolver": { 
       return ["resolveVariable": { FacesContext fc, String name -> 
       return map[name] 
       }] as VariableResolver 
      }] as Application 
      }, 
      "addMessage": {String key, FacesMessage val -> 
      println "added key: [${key}] value: [${val.getDetail()}] to JsfContext messages" 
      }, 
      "getMessages": {return null} 
     ] as FacesContext; 
     return fc; 
     } 
+0

Intéressant. Je pourrais avoir besoin de regarder de plus près Groovy. – BalusC

+0

le seul problème avec cette approche est que je dois ajouter toutes les méthodes que je veux utiliser à l'objet maquette – mkoryak

9

Vous pouvez retourner un contexte simulé par FacesContext.getCurrentInstance en invoquant setCurrentInstance(FacesContext) avant d'exécuter le test. La méthode est protégée, mais vous pouvez y accéder par réflexion ou par extension FacesContext. Il existe un exemple d'implémentation utilisant Mockito here.

3

Cette URL fournit un très bon article sur elle: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html

Vous avez votre bean géré:

package foo; 

import java.util.Map; 

import javax.faces.bean.ManagedBean; 
import javax.faces.bean.RequestScoped; 
import javax.faces.context.FacesContext; 

@ManagedBean 
@RequestScoped 
public class AlphaBean { 
    public String incrementFoo() { 
    Map<String, Object> session = FacesContext.getCurrentInstance() 
     .getExternalContext() 
     .getSessionMap(); 
    Integer foo = (Integer) session.get("foo"); 
    foo = (foo == null) ? 1 : foo + 1; 
    session.put("foo", foo); 
    return null; 
    } 
} 

Vous cognez le FacesContext:

package foo.test; 

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; 
    } 
} 

Ensuite, écrivez votre unité essai:

@Test 
    public void testIncrementFoo() { 
    FacesContext context = ContextMocker.mockFacesContext(); 
    try { 
     Map<String, Object> session = new HashMap<String, Object>(); 
     ExternalContext ext = mock(ExternalContext.class); 
     when(ext.getSessionMap()).thenReturn(session); 
     when(context.getExternalContext()).thenReturn(ext); 

     AlphaBean bean = new AlphaBean(); 
     bean.incrementFoo(); 
     assertEquals(1, session.get("foo")); 
     bean.incrementFoo(); 
     assertEquals(2, session.get("foo")); 
    } finally { 
     context.release(); 
    } 
    } 
4

Vous pouvez utiliser par exemple PowerMock qui est un cadre qui vous permet d'étendre les bibliothèques fictives comme Mockito avec des capacités supplémentaires. Dans ce cas, il vous permet de se moquer des méthodes statiques de FacesContext.

Si vous utilisez Maven, utilisez les codes link suivants pour vérifier la configuration de dépendance requise.

Annotez votre classe de test JUnit à l'aide de ces deux annotations. La première annotation indique à JUnit d'exécuter le test en utilisant PowerMockRunner. La deuxième annotation indique PowerMock pour se préparer à simuler la classe FacesContext.

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ FacesContext.class }) 
public class PageBeanTest { 

Mock FacesContext utilisant PowerMock et utiliser verify() de Mockito afin de vérifier que resolveVariable() a été appelé avec les paramètres attendus.

@Test 
public void testGetPageBean() { 
    // mock all static methods of FacesContext 
    PowerMockito.mockStatic(FacesContext.class); 

    FacesContext facesContext = mock(FacesContext.class); 
    when(FacesContext.getCurrentInstance()).thenReturn(facesContext); 

    Application application = mock(Application.class); 
    when(facesContext.getApplication()).thenReturn(application); 

    VariableResolver variableResolver = mock(VariableResolver.class); 
    when(application.getVariableResolver()).thenReturn(variableResolver); 

    PageBean.getPageBean("bean_reference"); 

    verify(variableResolver) 
      .resolveVariable(facesContext, "bean_reference"); 
} 

J'ai créé un blog post ce qui explique l'exemple de code ci-dessus plus en détail.

2

Je vous donne un exemple pour simuler FacesConext sans utiliser PowerMockito.L'idée est d'étendre une classe simple de FacesContext, et changer l'instance en cours en utilisant la méthode statique protégée setCurrentInstance:

import javax.faces.context.FacesContext; 
import javax.servlet.ServletContext; 

import org.junit.Before; 
import org.junit.Test; 
import org.mockito.Mock; 
import org.mockito.MockitoAnnotations; 

import com.sun.faces.config.InitFacesContext; 

public class DummyTest { 

    @Mock 
    private FacesContext context; 

    @Before 
    public void before(){ 
     MockitoAnnotations.initMocks(this); 
     ServletContext sc = mock(ServletContext.class); 
     new FakeContext(sc); 
     assertEquals(context, FacesContext.getCurrentInstance()); 
    } 

    @Test 
    public void dummy(){ 

    } 

    private class FakeContext extends InitFacesContext{ 

     public FakeContext(ServletContext sc) { 
      super(sc); 
      setCurrentInstance(context); 
     } 

    } 

} 
0

je crois que la meilleure solution n'est pas présentée ici. Ici, nous allons

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ FacesContext.class}) 
public class MyTestClass{ 

@Mock 
private FacesContext facesContext; 

@Before 
public void init() throws Exception { 
     PowerMockito.mockStatic(FacesContext.class); 
     PowerMockito.when(FacesContext.getCurrentInstance()).thenReturn(facesContext); 
} 

Et vous devez importer tout le paquet PowerMockito dans votre pom.xml

 <dependency> 
      <groupId>org.mockito</groupId> 
      <artifactId>mockito-all</artifactId> 
      <version>${mockito.version}</version> 
      <scope>test</scope> 
     </dependency> 
     <dependency> 
      <groupId>org.powermock</groupId> 
      <artifactId>powermock-api-mockito</artifactId> 
      <version>${powermock.version}</version> 
      <scope>test</scope> 
     </dependency> 
     <dependency> 
      <groupId>org.powermock</groupId> 
      <artifactId>powermock-module-junit4</artifactId> 
      <version>${powermock.version}</version> 
      <scope>test</scope> 
     </dependency> 
1

Voici une autre façon d'utiliser Mockito et de réflexion pour se moquer FacesContext et pour vous assurer que les appels normaux à FacesContext .getCurrentInstance() renvoie le (moquée) instance que vous souhaitez:

@Before 
public void setUp() { 

    // Use Mockito to make our Mocked FacesContext look more like a real one 
    // while making it returns other Mocked objects 
    ExternalContext externalContext = Mockito.mock(ExternalContext.class); 
    Flash flash = Mockito.mock(Flash.class); 
    FacesContext facesContext = Mockito.mock(FacesContext.class); 
    Mockito.when(facesContext.getExternalContext()).thenReturn(externalContext); 
    Mockito.when(externalContext.getFlash()).thenReturn(flash); 

    // Use Java reflection to set the FacesContext to our Mock, since 
    // FacesContext.setCurrentInstance() is protected. 
    try { 
     Method setter = FacesContext.class.getDeclaredMethod("setCurrentInstance", new Class[]{FacesContext.class}); 
     setter.setAccessible(true); 
     setter.invoke(null, new Object[]{facesContext}); 
    } catch (Exception e) { 
     System.err.println("Exception in reflection-based access to FacesContext"); 
     e.printStackTrace(); 
    } 
} 

(Ceci est adapté/étendue de @ réponse de McDowell ci-dessous.)