Mocking FacesContext - PullRequest
       42

Mocking FacesContext

18 голосов
/ 23 ноября 2010

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

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

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

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

Ответы [ 7 ]

13 голосов
/ 26 января 2012

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

8 голосов
/ 27 февраля 2014

Этот 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();
    }
  }
6 голосов
/ 09 ноября 2014

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

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

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

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

Mock FacesContext с использованием PowerMock и использованием 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");
}

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

3 голосов
/ 19 ноября 2014

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

    }

}
2 голосов
/ 28 сентября 2017

Вот еще один способ использовать 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 ниже.)

2 голосов
/ 02 декабря 2010

в моем случае я смогла высмеять его в чистом виде.я предоставляю карту 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 голосов
/ 22 сентября 2016

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

@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>
...