Mocking FacesContext

Я пытаюсь добавить некоторые модульные тесты в приложение JSF. Это приложение не опиралось в большой степени на лучшие практики, поэтому многие методы обслуживания используют FacesContext получить данные из управляемых сессионных компонентов следующим образом:

(это внутри утилитарного класса)

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

Что было бы лучшим способом высмеять это? Я использую Groovy, поэтому у меня есть еще несколько вариантов для создания классов, которые я не могу обычно создавать.

7 ответов

Решение

В моем случае я смогла высмеять это в чистом виде. Я предоставляю карту 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;
      }

Вы можете вернуть фиктивный контекст через FacesContext.getCurrentInstance вызывая setCurrentInstance(FacesContext) перед запуском теста. Метод защищен, но вы можете получить к нему доступ через отражение или расширение FacesContext, Здесь приведен пример реализации с использованием Mockito.

Этот URL содержит действительно хорошую статью: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html

У вас есть управляемый боб:

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

Вы можете использовать, например, PowerMock, который является фреймворком, который позволяет расширять библиотеки-макеты, такие как Mockito, с дополнительными возможностями В этом случае это позволяет вам высмеивать статические методы FacesContext,

Если вы используете Maven, используйте следующую ссылку, чтобы проверить необходимые настройки зависимостей.

Аннотируйте свой тестовый класс JUnit, используя эти две аннотации. Первая аннотация говорит JUnit запустить тест, используя PowerMockRunner, Вторая аннотация говорит PowerMock подготовиться к издевательству над FacesContext учебный класс.

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

издеваться FacesContext используя PowerMock и использовать verify() Мокито для того, чтобы проверить, что 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");
}

Я создал пост в блоге, который объясняет приведенный выше пример кода более подробно.

Вот еще один способ использовать Mockito и рефлексию для насмешки FacesContext и чтобы убедиться, что обычные вызовы FacesContext.getCurrentInstance() возвращают (mocked) экземпляр, который вы хотите:

@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 ниже.)

Я привожу вам пример для насмешки 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);
        }

    }

}

Я считаю, что лучшее решение не представлено здесь. Вот так

@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>
Другие вопросы по тегам