Подтвердить что ты не робот

Как подделать InitialContext с помощью конструктора по умолчанию

Все,

Я пытаюсь выполнить некоторые модульные тесты в каком-то архаичном Java-коде (без интерфейсов, без абстракции и т.д.)

Это сервлет, который использует ServletContext (который я предполагаю, настроен Tomcat), и он имеет информацию о базе данных, которая настроена в файле web.xml/context.xml. Теперь я понял, как сделать Fake ServletContext, но код имеет

 InitialContext _ic = new InitialContext();

по всему месту (поэтому заменить его не представляется возможным). Мне нужно найти способ сделать по умолчанию InitialContext() способным сделать _ic.lookup(val) без исключения исключения.

Я предполагаю, что есть некоторый способ загрузки context.xml, но как работает эта магия, я рисую пробел. У кого-нибудь есть идеи?

4b9b3361

Ответ 1

Вы можете использовать PowerMock, чтобы искупить построение InitialContext и контролировать его поведение. Конструктор Mocking документирован здесь.

Тесты PowerMock могут быть довольно грязными и сложными, рефакторинг обычно является лучшим вариантом.

Ответ 2

Воспользуйтесь тем фактом, что InitialContext использует SPI для управления его созданием. Вы можете подключиться к его жизненному циклу, создав реализацию javax.naming.spi.InitialContextFactory и передав ее тестам через системное свойство javax.naming.factory.initial (Context.INTITIAL_CONTEXT_FACTORY). Это проще, чем кажется.

Учитывая этот класс:

public class UseInitialContext {

    public UseInitialContext() {
        try {
            InitialContext ic = new InitialContext();
            Object myObject = ic.lookup("myObject");
            System.out.println(myObject);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }


} 

И это подразумевает InitialContextFactory:

public class MyInitialContextFactory implements InitialContextFactory {

    public Context getInitialContext(Hashtable<?, ?> arg0)
            throws NamingException {

        Context context = Mockito.mock(Context.class);
        Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!");
        return context;
    }
}

Создание экземпляра UseInitialContext в тесте junit с помощью

-Djava.naming.initial.factory=initial.context.test.MyInitialContext

на выходах командной строки This is my object!! (легко настроить в затмении). Мне нравится Mockito за насмешки и огрызки. Я бы также порекомендовал Micheal Feather эффективно работать с устаревшим кодом, если вы имеете дело с большим количеством устаревшего кода. Это все о том, как найти швы в программах, чтобы выделить отдельные части для тестирования.

Ответ 3

Здесь мое решение для настройки Inintial Context для моих модульных тестов. Сначала я добавил следующую зависимость теста к моему проекту:

<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>catalina</artifactId>
  <version>6.0.33</version>
  <scope>test</scope>
</dependency>

Затем я создал статический метод со следующим кодом:

public static void setupInitialContext() throws Exception {
    System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
    System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
    InitialContext ic = new InitialContext();
    ic.createSubcontext("jdbc");
    PGSimpleDataSource ds = new PGSimpleDataSource();
    ds.setDatabaseName("postgres");
    ds.setUser("postgres");
    ds.setPassword("admin");
    ic.bind("jdbc/something", ds);
}

Наконец, в каждом из моих тестовых классов я добавляю метод @BeforeClass, который вызывает setupInitialContext.

Ответ 4

Попробуйте настроить системные переменные до:

System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
        "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
        "org.apache.naming");
InitialContext ic = new InitialContext();

Если вы используете JUnit, следуйте этому документу: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit

Ответ 5

Сегодня я столкнулся с той же проблемой (мы не можем использовать PowerMock) и решил ее так:

  • Не выполняйте поиск в конструкторе, поэтому, когда вы вызываете @InitMock на объект, конструктор еще не требует контекста.

  • Создайте метод для извлечения службы bean, если необходимо, как "getService(). serviceMethod (param, param...)":

    /* Class ApplicationResourceProvider */

    /* We can mock this and set it up with InjectMocks */
    InitialContext ic;

    /* method hiding the lookup */
    protected ApplicationService getService() throws NamingException {
        if(ic == null)
            ic = new InitialContext();
        return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal");
    }
  1. В тесте установите его:
@Mock
ApplicationService applicationServiceBean;

@Mock
InitialContext ic;

@InjectMocks
ApplicationResourceProvider arp;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    when(ic.lookup(anyString())).thenReturn(applicationServiceBean);
    ...
}

Ответ 6

Вы считали mockito?

Это просто:

InitialContext ctx = mock (InitialContext.class);

Кстати, если вы решите использовать макеты, я бы порекомендовал также прочитать эту статью: http://martinfowler.com/articles/mocksArentStubs.html

Ответ 7

Бедная автономная реализация без внешних библиотек:

public class myTestClass {
    public static class TestContext extends InitialContext {
        public TestContext() throws NamingException {
            super(true /*prevents initialization*/);
        }

        static Object someExpectedValue = "the expected string or object instance";

        /*override the method(s) called by the legacy program on _ic, check the parameter and return the wanted value */
        public Object lookup(String name) throws NamingException {
            return name != null && name.equals("theValueOfVal") ? someExpectedValue : null;
        }
    }

    public static class TestInitialContextFactory implements InitialContextFactory {
        public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
            return new TestContext();
        }
    }

    public static void main(String[] args) throws SQLException {
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "the.package.myTestClass$TestInitialContextFactory");
        /*now call the legacy logic to be tested*/
        ...

Вы можете использовать switch в переопределении метода lookup, чтобы вернуть ожидаемое значение для каждого отдельного значения val, переданного _ic.lookup(val) в рамках прежней программы.