Насмешка над URL в Java - PullRequest
       17

Насмешка над URL в Java

20 голосов
/ 19 февраля 2009

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

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

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

Ответы [ 10 ]

20 голосов
/ 31 мая 2011

Как сказал Роб, если вы хотите смоделировать соединение, возвращаемое с 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");
19 голосов
/ 19 февраля 2009

Когда у меня есть класс, который не может быть легко смоделирован, потому что он является окончательным (или запечатан в C #), мой обычный путь - написать обертку вокруг класса и использовать обертку везде, где я буду использовать реальный класс. Тогда я бы по необходимости высмеивал класс-обёртку.

8 голосов
/ 18 января 2013

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

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, который содержит мои фиктивные данные.

7 голосов
/ 19 февраля 2009

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

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

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

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

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

6 голосов
/ 16 октября 2016

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

Зарегистрировать 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: я не знаю, как отменить автопробел для нумерованного списка после последней строки

4 голосов
/ 21 февраля 2013

Я думаю, вы можете использовать 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);
    }
}
2 голосов
/ 14 июня 2009

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

Похоже, что 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-агента при запуске без использования Attach API. Это должно работать в Apple JDK 1.5 / 1.6.

2 голосов
/ 19 февраля 2009

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

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

1 голос
/ 22 ноября 2016

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

final URL url = 
    new URL("file://" + getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
0 голосов
/ 19 февраля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...