2010-11-23 151 views
13

我想向JSF應用程序添加一些單元測試。此應用程序didnt在很大程度上依賴於任何的最佳做法,讓很多服務方法使用FacesContext從管理會話bean中提取數據,就像這樣:嘲笑FacesContext

(這是一個實用程序類中)

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

,會是什麼最好的方法來嘲笑這個?我使用groovy,所以我有更多的選項來創建我通常無法創建的類。

回答

2

在我的情況下,我能夠純粹的groovy嘲笑它。 我提供地圖MockBeans的,它可以返回:

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

有趣。我可能需要仔細看看Groovy。 – BalusC 2010-12-02 14:31:59

9

您可以通過運行測試之前調用setCurrentInstance(FacesContext)通過FacesContext.getCurrentInstance返回一個模擬環境。該方法受到保護,但您可以通過反射或延伸FacesContext來訪問它。有一個示例實現使用Mockito here

3

該網址提供了它一個很好的文章: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html

你有你的託管bean:

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

你存根出在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; 
    } 
} 

然後再編寫單元測試:

@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

您可以使用例如PowerMock這是一個框架,允許您擴展模擬庫,如Mockito具有額外的功能。在這種情況下,它允許您模擬FacesContext的靜態方法。

如果您使用的是Maven,請使用以下link來檢查所需的依賴項設置。

使用這兩個批註註釋您的JUnit測試類。第一個註釋告訴JUnit使用PowerMockRunner運行測試。第二個註釋告訴PowerMock準備模擬FacesContext類。使用

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

模擬FacesContextPowerMock和使用verify()的Mockito爲了檢查resolveVariable()被稱爲與預期的參數。

@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"); 
} 

我已經創建了一個blog post其中更詳細地解釋上面的代碼示例。

2

我給你一個例子來模擬FacesConext而不使用PowerMockito。這個想法是從FacesContext中擴展一個簡單的類,並使用受保護的靜態方法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

我認爲最好的解決方法這裏就不介紹了。在這裏,我們去

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

而你需要導入所有的PowerMockito捆綁在你的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

這裏的另一種方式來使用和的Mockito反射嘲笑的FacesContext和作出的FacesContext確保正常通話.getCurrentInstance()返回你想要的(嘲笑)實例:

@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(); 
    } 
} 

(這是改編/從下面@麥克道爾的回答延長。)