13

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

Все,

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

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

 InitialContext _ic = new InitialContext();

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

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

  • Вместо «java.naming.initial.factory» я думаю, что правильным системным свойством является «java.naming.factory.initial». По крайней мере, в java 6. Спасибо за пост!

    от
  • Context.INITIAL_CONTEXT_FACTORY, поэтому вы не хотите запоминать фактическое строковое значение для имени переменной среды фабрики исходного контекста.

    от
  • Так что, если я использую JBoss, я думаю, что я должен использовать среду JBoss. Спасибо!

    от
  • +1. PowerMock является мощным средством, но оно определенно вызывает достаточную головную боль, поэтому рефакторинг является наиболее предпочтительным.

    от
  • FWIW, я написал очень похожий ответ на очень похожий вопрос через некоторое время после этого, и я сделал этоusing a JUnit TestTule to handle the setup and teardown.

    от
  • Избегайте PowerMock, если у вас нет другой открытой опции ... Множество статей, объясняющих почему.

    от
  • Я думаю, что это лучшее решение для того, что я пытаюсь сделать. Спасибо!

    от Austin
  • вы должны иметь среду tomcat в вашем проекте. Вы должны добавить банки кота

    от
  • Ссылка на блог оракула не работает

    от
  • Это даетjava.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory

    от
  • по отношению к этомуblogs.oracle.com/randystuph/entry/… было очень хорошо

    от
  • & APOS; java.naming.factory.initial & APOS; также для Java 7.

    от
  • То, что это происходит много, не означает, что его невозможно заменить. Черт возьми, даже просто перейти на использование статического метода фабрики позволитmore тестируемость (хотя это явно не так хорошо, как некоторые альтернативы).

    от Jon Skeet
6 ответов
  • 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

  • 0

    Вы рассматривали

    Mockito?

    Это так просто, как:

    InitialContext ctx = mock(InitialContext.class);

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

  • 3

    Ты можешь использовать

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

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

  • 1

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

    rMock) и решил ее следующим образом:

    Don't lookup in the constructor so when you invoke @InitMock on the object, the constructor doesn't require the context yet.

    Create a method for retrieving the service bean when needed like "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");
        }
    
    On the test, set it up:
    @Mock
    ApplicationService applicationServiceBean;
    
    @Mock
    InitialContext ic;
    
    @InjectMocks
    ApplicationResourceProvider arp;
    
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        when(ic.lookup(anyString())).thenReturn(applicationServiceBean);
        ...
    }
    

  • 35

    Воспользуйтесь тем, что

    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 в тесте джунит с

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

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

  • 6

    Вот мое решение по настройке начального контекста для моих модульных т

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

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