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

时间:2018-09-06 17:18:49

标签: 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();
    }
}

一种方法将包装器用于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());
    }
}

2 个答案:

答案 0 :(得分:5)

模拟静态方法始终是一种脆弱的方法。如果可能的话,最好使用非静态的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()分离。如果以后您确定需要使用版本2 UUID(基于日期时间)或某些其他版本,而不是随机的UUID,则也可以在生产代码中轻松更改它。这就是人们所说的,使您的代码更具可测试性通常可以带来更好的总体设计。

答案 1 :(得分:2)

因此,我拿走了您的代码并使它生效。您需要做的就是将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_指出了。