简体   繁体   English

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

[英]How to mock UUID.randomUUID() from java.util package?

What is wrong with the UUID.randomUUID() - it just can't be mocked UUID.randomUUID()有什么问题 - 它不能被UUID.randomUUID()

Is it possible to mock? 有可能嘲笑吗? Or i have an error in my source? 或者我的来源有错误?

Look at example: 看一下例子:

1) Class that is tested 1)被测试的类

package com.grayen;

import java.util.UUID;

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

One method uses a wrapper for UUID and i can mock that wrapper! 一种方法使用UUID的包装器,我可以模拟该包装器!

2) Wrapper for real UUID (all modifiers the same) 2)真正UUID的包装(所有修饰符相同)

package com.grayen;

import java.util.UUID;

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

3) Testing (last commented line throws an exception) 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());
    }
}

Mocking a static method is always a fragile approach. 模拟静态方法总是一种脆弱的方法。 If possible, prefer to use a non-static UUID source, which then can be easily mocked. 如果可能,请使用非静态UUID源,然后可以轻松模拟。

For example: 例如:

/**
 * 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();
}

Then you can inject it into TestedClass , or have TestedClass have a private member: 然后你可以将它注入TestedClass ,或让TestedClass有一个私有成员:

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

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

Then to test it, you can either have a test-only constructor, to allow injecting in a mocked UuidSource , or you could replace the value of the uuidSource field directly (either by widening its visibility or by using reflection or something). 然后要测试它,你可以有一个只测试的构造函数,允许注入一个UuidSource ,或者你可以直接替换uuidSource字段的值(通过扩大其可见性或使用反射或其他东西)。

As a bonus: this decouples your actual production code from UUID.randomUUID() . 作为奖励:这将您的实际生产代码与UUID.randomUUID() If later you decide that you need to use version 2 UUIDs (datetime-based) or some other version rather than random UUIDs, you can easily change that in your production code as well. 如果以后您决定需要使用版本2 UUID(基于日期时间)或其他版本而不是随机UUID,您也可以轻松地在生产代码中更改它。 This is what people mean when they say that making your code more testable usually leads to a better overall design. 当人们说使代码更易于测试时,这就是人们的意思,这通常会带来更好的整体设计。

So I took your code and got it to work. 所以我拿了你的代码并开始工作。 All you need to do is add TestedClass.class to your @PrepareForTest . 您需要做的就是将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());
    }
}

Edit 编辑

I did not see the comment, but k5_ pointed it out. 我没有看到评论,但k5_指出了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM