web-dev-qa-db-ja.com

Java.utilパッケージからUUID.randomUUID()をモックする方法は?

UUID.randomUUID()の何が問題になっているのですか-あざけることができません

モックすることは可能ですか?または、ソースにエラーがありますか?

例を見てください:

1)テストされるクラス

package com.grayen;

import Java.util.UUID;

public class TestedClass {
    public UUID getUuid() {
        return UUID.randomUUID();
    }
    public UUID getUuidFromWrapper() {
        return UuidWrapper.randomUUID();
    }
}

1つの方法は、UUIDのラッパーを使用し、そのラッパーをモックできます!

2)実際のUUIDのラッパー(すべての修飾子は同じ)

package com.grayen;

import Java.util.UUID;

public final class UuidWrapper {
    public static UUID randomUUID() {
        return UUID.randomUUID();
    }
}

3)テスト(最後にコメントされた行で例外がスローされます)

package com.grayen;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import Java.util.UUID;
import static org.junit.Assert.assertEquals;

@PrepareForTest({UUID.class, UuidWrapper.class})
@RunWith(PowerMockRunner.class)
public class TestedClassTest {

    @Test
    public void testMethod() {
        UUID uuid = UUID.randomUUID();

        PowerMockito.mockStatic(UUID.class);
        PowerMockito.mockStatic(UuidWrapper.class);

        PowerMockito.when(UUID.randomUUID()).thenReturn(uuid);
        PowerMockito.when(UuidWrapper.randomUUID()).thenReturn(uuid);

        TestedClass testedClass = new TestedClass();

        assertEquals(uuid, testedClass.getUuidFromWrapper());
        //assertEquals(uuid, testedClass.getUuid());
    }
}
5
Maksim Petrov

静的メソッドのモックは常に脆弱なアプローチです。可能であれば、静的でないUUIDソースを使用することをお勧めします。これにより、簡単にモックを作成できます。

例えば:

_/**
 * A source of new {@link UUID} instances.
 */
public interface UuidSource {
    /**
     * Returns a new {@link UuidSource} that generates UUIDs using {@link UUID#randomUUID}.
     */
    public static UuidSource random() {
        return UUID::randomUUID;
    }

    /**
     * Returns a new {@link UUID} instance.
     *
     * <p>The returned value is guaranteed to be unique.
     */
    UUID newUuid();
}
_

次に、それをTestedClassに注入するか、TestedClassにプライベートメンバーを含めることができます。

_public class TestedClass {
    private UuidSource uuidSource = UuidSource.random();

    public UUID getUUID() {
        return uuidSource.newUuid();
    }
    // etc.
}
_

次に、それをテストするには、モックされたUuidSourceに注入できるように、テスト専用コンストラクターを用意するか、uuidSourceフィールドの値を直接置き換えます(可視性を拡大することにより)または反射または何かを使用して)。

おまけとして、これは実際の製品コードをUUID.randomUUID()から切り離します。後で、ランダムなUUIDではなく、バージョン2のUUID(日時ベース)または他のバージョンを使用する必要があると判断した場合は、プロダクションコードでも簡単に変更できます。これは、コードをよりテストしやすくすることで通常、全体的なデザインが改善されると人々が言っ​​ていることを意味しています。

6
Daniel Pryden

だから私はあなたのコードを取り、それを動作させました。 TestedClass.class@PrepareForTestに追加するだけです。

package com.grayen;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import Java.util.UUID;
import static org.junit.Assert.assertEquals;

@PrepareForTest({
    UUID.class, 
    UuidWrapper.class, 
    TestedClass.class
})
@RunWith(PowerMockRunner.class)
public class TestedClassTest {

    @Test
    public void testMethod() {
        UUID uuid = UUID.randomUUID();

        PowerMockito.mockStatic(UUID.class);
        PowerMockito.mockStatic(UuidWrapper.class);

        PowerMockito.when(UUID.randomUUID()).thenReturn(uuid);
        PowerMockito.when(UuidWrapper.randomUUID()).thenReturn(uuid);

        TestedClass testedClass = new TestedClass();

        assertEquals(uuid, testedClass.getUuidFromWrapper());
        assertEquals(uuid, testedClass.getUuid());
    }
}

編集

私はコメントを見ませんでした、しかし k5 _ はそれを指摘しました。

3
CodeMonkey