web-dev-qa-db-ja.com

FacesContextをモックする

JSFアプリケーションにいくつかの単体テストを追加しようとしています。このアプリケーションはベストプラクティスに大きく依存していなかったため、多くのサービスメソッドはFacesContextを使用して次のようにマネージドセッションBeanからデータをプルします。

(これはutilクラス内にあります)

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

これをモックする最良の方法は何でしょうか?私はgroovyを使用しているので、通常は作成できないクラスを作成するためのオプションがいくつかあります。

18
mkoryak

私の場合、純粋なグルーヴィーでそれをモックすることができました。私はそれが返すことができる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;
      }
2
mkoryak

テストを実行する前に setCurrentInstance(FacesContext) を呼び出すことにより、FacesContext.getCurrentInstanceを介してモックコンテキストを返すことができます。メソッドは保護されていますが、リフレクションを介して、またはFacesContextを拡張することでアクセスできます。 Mockito here を使用したサンプル実装があります。

13
McDowell

このURLはそれに関する本当に良い記事を提供します: 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();
    }
  }
8
Alan B. Dee

たとえば、PowerMockを使用できます。これは、Mockitoのようなモックライブラリを追加機能で拡張できるフレームワークです。この場合、FacesContextの静的メソッドをモックすることができます。

Mavenを使用している場合は、次の link を使用して、必要な依存関係の設定を確認してください。

これらの2つのアノテーションを使用して、JUnitテストクラスにアノテーションを付けます。最初のアノテーションは、PowerMockRunnerを使用してテストを実行するようにJUnitに指示します。 2番目のアノテーションは、PowerMockFacesContextクラスをモックする準備をするように指示します。

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

PowerMockを使用してFacesContextをモックし、Mockitoverify()を使用して、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");
}
_

上記のコードサンプルをより詳細に説明する ブログ投稿 を作成しました。

6
CodeNotFound

PowerMockitoを使用せずにFacesConextをモックする例を示します。アイデアは、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);
        }

    }

}
3
ravenskater

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

(これは、以下の@McDowellの回答から適応/拡張されています。)

2
Sarah Messer

ここでは最善の解決策は提示されていないと思います。さあ行こう

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

そして、pom.xmlにすべてのPowerMockitoバンドルをインポートする必要があります

        <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>
0
pmartin8