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

Издевательский URL в Java

У нас есть объект URL в одном из наших классов Java, который мы хотим высмеять, но это последний класс, поэтому мы не можем. Мы не хотим идти выше уровня и издеваться над InputStream, потому что это все равно оставит нас с непроверенным кодом (у нас есть драконовские стандарты тестирования).

Я пробовал jMockIt отражающие возможности, но мы работаем над Mac, и есть проблемы с обработчиком Java-агента, которые я не смог решить.

Итак, есть ли какие-либо решения, которые не связаны с использованием реальных URL-адресов в тесте junit?

4b9b3361

Ответ 1

Когда у меня есть класс, который нелегко высмеивать, потому что он является окончательным (или запечатанным на С#), мой обычный маршрут состоит в том, чтобы написать оболочку вокруг класса и использовать обертку, где бы я не использовал фактический класс. Затем я бы высмеивал класс обертки по мере необходимости.

Ответ 2

Как сказал Роб, если вы хотите издеваться над соединением, возвращаемым с URL-адреса, вы можете расширить URLStreamHandler. Например, с mockito:

final URLConnection mockUrlCon = mock(URLConnection.class);

ByteArrayInputStream is = new ByteArrayInputStream(
        "<myList></myList>".getBytes("UTF-8"));
doReturn(is).when(mockUrlCon).getInputStream();

//make getLastModified() return first 10, then 11
when(mockUrlCon.getLastModified()).thenReturn((Long)10L, (Long)11L);

URLStreamHandler stubUrlHandler = new URLStreamHandler() {
    @Override
     protected URLConnection openConnection(URL u) throws IOException {
        return mockUrlCon;
     }            
};
URL url = new URL("foo", "bar", 99, "/foobar", stubUrlHandler);
doReturn(url).when(mockClassloader).getResource("pseudo-xml-path");

Ответ 3

Я использовал URLHandler, который позволяет мне загружать URL-адрес из пути к классам. Итак, следующее

new URL("resource:///foo").openStream()

откроет файл с именем foo из пути к классу. Для этого я использую общую библиотеку утилиты и регистрирую обработчик. Чтобы использовать этот обработчик, вам просто нужно позвонить:

com.healthmarketscience.common.util.resource.Handler.init();

и URL ресурса теперь доступен.

Ответ 4

Я пошел со следующим:

public static URL getMockUrl(final String filename) throws IOException {
    final File file = new File("testdata/" + filename);
    assertTrue("Mock HTML File " + filename + " not found", file.exists());
    final URLConnection mockConnection = Mockito.mock(URLConnection.class);
    given(mockConnection.getInputStream()).willReturn(
            new FileInputStream(file));

    final URLStreamHandler handler = new URLStreamHandler() {

        @Override
        protected URLConnection openConnection(final URL arg0)
                throws IOException {
            return mockConnection;
        }
    };
    final URL url = new URL("http://foo.bar", "foo.bar", 80, "", handler);
    return url;
}

Это дает мне реальный объект URL, содержащий мои макетные данные.

Ответ 5

Я думаю, вы можете использовать Powermock для этого. Я смог высмеять URL-класс, используя PowerMock в последнее время. Надеюсь, это поможет.

/* Фактический класс */

import java.net.MalformedURLException;
import java.net.URL;

public class TestClass {

    public URL getUrl()
        throws MalformedURLException {

        URL url = new URL("http://localhost/");
        return url;
    }
}

/* Класс тестирования */

import java.net.URL;

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(value = { TestClass.class })
public class TestClassTest {

    private TestClass testClass = new TestClass();

    @Test
    public void shouldReturnUrl()
        throws Exception {

        URL url = PowerMockito.mock(URL.class);
        PowerMockito.whenNew(URL.class).withParameterTypes(String.class)
                .withArguments(Mockito.anyString()).thenReturn(url);
        URL url1 = testClass.getUrl();
        Assert.assertNotNull(url1);
    }
}

Ответ 6

Если вы не хотите создавать обертку:

Зарегистрировать URLStreamHandlerFactory

Сделайте способ, которым вы хотите публиковать

Откупите цепочку

abstract public class AbstractPublicStreamHandler extends URLStreamHandler {
    @Override
    public URLConnection openConnection(URL url) throws IOException {
        return null;
    }
}

public class UrlTest {
    private URLStreamHandlerFactory urlStreamHandlerFactory;

    @Before
    public void setUp() throws Exception {
        urlStreamHandlerFactory = Mockito.mock(URLStreamHandlerFactory.class);
        URL.setURLStreamHandlerFactory(urlStreamHandlerFactory);
    }

    @Test
    public void should_return_mocked_url() throws Exception {
        // GIVEN
        AbstractPublicStreamHandler publicStreamHandler = Mockito.mock(AbstractPublicStreamHandler.class);
        Mockito.doReturn(publicStreamHandler).when(urlStreamHandlerFactory).createURLStreamHandler(Matchers.eq("http"));

        URLConnection mockedConnection = Mockito.mock(URLConnection.class);
        Mockito.doReturn(mockedConnection).when(publicStreamHandler).openConnection(Matchers.any(URL.class));

        Mockito.doReturn(new ByteArrayInputStream("hello".getBytes("UTF-8"))).when(mockedConnection).getInputStream();

        // WHEN
        URLConnection connection = new URL("http://localhost/").openConnection();

        // THEN
        Assertions.assertThat(new MockUtil().isMock(connection)).isTrue();
        Assertions.assertThat(IOUtils.toString(connection.getInputStream(), "UTF-8")).isEqualTo("hello");
    }
}

PS: Я не знаю, как отменить автоматический интервал между пронумерованными списками после последней строки

Ответ 7

Я бы снова посмотрел, почему вы хотите высмеять конечный объект данных. Поскольку по определению вы не подклассифицируете объект в своем действительном коде, и он не будет объектом, находящимся под тестированием, вам не нужно будет проверять этот код в белом ящике; просто передайте любые (реальные) URL-объекты и проверьте вывод.

Макетные объекты полезны, когда трудно создать подходящий объект, или метод реального объекта либо трудоемкий, либо зависит от некоторого внешнего ресурса с сохранением состояния (например, базы данных). Ни один из них не применяется в этом случае, поэтому я не понимаю, почему вы не можете просто создать настоящий URL-объект, представляющий соответствующее местоположение ресурса.

Ответ 8

JMockit действительно позволяет вам высмеивать финальный класс JRE, например java.net.URL.

Кажется, API-интерфейс Attach API в jdkDir/lib/tools.jar, доступный в реализациях JDK 1.6, отличных от Sun, тоже не работает. Я предполагаю, что этот материал все еще слишком новый/продвинутый или просто не получил необходимого внимания от других поставщиков JDK (Apple, IBM с J9 JDK, Oracle с JRockit JDK).

Итак, если вы столкнулись с проблемами с помощью tools.jar в пути к классам, попробуйте использовать аргумент JVM "-javaagent: jmockit.jar". Он сообщает JVM напрямую загружать java-агент при запуске без использования API-интерфейса Attach. Это должно работать в Apple JDK 1.5/1.6.

Ответ 9

Создайте URL-объект, указывающий на сам тестовый класс.

final URL url = 
    new URL("file://" + getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

Ответ 10

Является ли класс класса интерфейсом? Если это так, вы можете создать экземпляр с помощью инверсии управления или настраиваемого factory, а не путем прямой конструкции, это позволит вам вводить/создавать тестовый экземпляр во время тестирования, а не последний экземпляр, который у вас есть.