web-dev-qa-db-ja.com

Java)でURLをモックする

模擬したいJavaクラスの1つにURLオブジェクトがありますが、これは最終クラスなのでできません。上のレベルに移動したくないので、InputStreamを模擬します。それでもテストされていないコードが残ります(厳格なテストカバレッジ基準があります)。

JMockItの反射力を試しましたが、Macで動作し、Javaエージェントハンドラーに問題があり、解決できませんでした。

では、junitテストで実際のURLを使用しないソリューションはありますか?

21
bowsie

最終的な(またはC#で封印されている)ために簡単にモックできないクラスがある場合、通常のルートは、クラスの周りにラッパーを記述し、実際のクラスを使用する場所でラッパーを使用することです。次に、必要に応じてラッパークラスをモックアウトします。

19
tvanfosson

Robが言ったように、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");
20
Artefacto

私は次のように行きました:

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オブジェクトが得られます。

10
koljaTM

クラスパスからURLをロードできるURLHandlerを使用しました。だから次の

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

クラスパス内からfooという名前のファイルを開きます。これを行うには、 共通ユーティリティライブラリ を使用してハンドラーを登録します。このハンドラーを使用するには、次を呼び出す必要があります。

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

これでリソースURLが利用可能になりました。

7
Rob Di Marco

ラッパーを作成したくない場合:

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:最後の行の後に番号付きリストの自動間隔をキャンセルする方法がわかりません

6
api

Powermockを使用してこれを行うことができると思います。最近、PowerMockを使用してURLクラスをモックすることができました。お役に立てれば。

/ *実際のクラス* /

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);
    }
}
4
Richards Peter.

JMockitでは、Java.net.URLのような最終的なJREクラスをモックすることができます。

Sun以外のJDK1.6の実装で使用可能なjdkDir/lib /tools.jarのAttachAPIも機能しないようです。このようなものはまだ新しすぎたり、進んだりしているか、他のJDKベンダー(Apple、IBMとJ9 JDK、OracleとJRockit JDK)から必要な注目を集めていなかったと思います。

したがって、クラスパスにtools.jarを含めることで問題が発生した場合は、「-javaagent:jmockit.jar」JVM引数を使用してみてください。 AttachAPIを使用せずに起動時にJavaエージェントを直接ロードするようにJVMに指示します。これはApple JDK 1.5 /1.6で機能するはずです。

2
Rogério

最終的なデータオブジェクトをモックする理由をもう一度見てみましょう欲しい。定義上、実際のコードでオブジェクトをサブクラス化することはなく、テスト対象のオブジェクトになることもないため、このコードをホワイトボックステストする必要はありません。適切な(実際の)URLオブジェクトを渡して、出力を確認するだけです。

モックオブジェクトは、適切な実オブジェクトを作成するのが難しい場合、または実オブジェクトのメソッドに時間がかかるか、ステートフルな外部リソース(データベースなど)に依存している場合に役立ちます。この場合、どちらも当てはまらないため、適切なリソースの場所を表す実際のURLオブジェクトを作成できない理由がわかりません。

2
Andrzej Doyle

テストクラス自体を指すURLオブジェクトを作成します。

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

URLクラスはインターフェースを実装していますか?その場合、直接構築するのではなく、制御の反転または構成可能なファクトリを使用してインスタンス化できます。これにより、現在の最終インスタンスではなく、テスト実行時にテストインスタンスを注入/構築できます。

0
Joel