如何从java.util包中模拟UUID.randomUUID()?

Mak*_*rov 5 java junit4 powermockito

有什么问题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();
    }
}
Run Code Online (Sandbox Code Playgroud)

一种方法使用UUID的包装器,我可以模拟该包装器!

2)真正UUID的包装(所有修饰符相同)

package com.grayen;

import java.util.UUID;

public final class UuidWrapper {
    public static UUID randomUUID() {
        return UUID.randomUUID();
    }
}
Run Code Online (Sandbox Code Playgroud)

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*den 6

模拟静态方法总是一种脆弱的方法.如果可能,请使用非静态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();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以将它注入TestedClass或拥有TestedClass私人成员:

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

    public UUID getUUID() {
        return uuidSource.newUuid();
    }
    // etc.
}
Run Code Online (Sandbox Code Playgroud)

然后要测试它,你可以有一个只测试构造函数,允许在mocked中注入UuidSource,或者你可以uuidSource直接替换字段的值(通过扩大其可见性或使用反射或其他东西).

作为奖励:这将您的实际生产代码与之分离UUID.randomUUID().如果以后您决定需要使用版本2 UUID(基于日期时间)或其他版本而不是随机UUID,您也可以轻松地在生产代码中更改它.当人们说使代码更易于测试时,这就是人们的意思,这通常会带来更好的整体设计.

  • 我通常将此构造用于UUID或Instant公共类TestedClass {private Supplier <UUID> uuidSupplier = UUID :: randomUUID; public void foo(){UUID uuid = uuidSupplier.get(); }} (2认同)

Cod*_*key 5

所以我拿了你的代码并让它工作。您所需要做的就是添加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());
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

我没有看到评论,但k5_指出了。