mockito 我如何模拟一个方法,它有一个带私有构造函数的参数?

ilmyapht  于 2022-11-08  发布在  其他
关注(0)|答案(1)|浏览(219)

我有一个抽象缓存客户端,它有一个实现,我试图向其中添加单元测试,它有一个键的受保护类实现。

public abstract class SimpleCacheClient<V extends Serializable> {
    // Autowired RedissonClient and RedisKeyGenerator

    public V get(SimpleCacheKey key) {
        // return the cache entry from the autowired RedissonClient
    }

    public void set(SimpleCacheKey key, V value) {
        // set the cache entry
    }

    public SimpleCacheKey getCacheKey(Object...keys) {
        return new SimpleCacheKey(keyGenerator.generateKey(keys));
    }

    /**
    * Simple wrapper for cache key to guarantee that implementations
    * are using the key generator provided in this class
    */
    protected class SimpleCacheKey {
        private String key;

        SimpleCacheKey(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }

        @Override
        public String toString() {
            return getKey();
        }
    }
}

下面是我尝试测试的实现:

public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
    public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
        // Do stuff with eventDateTimes and then
        set(getCacheKey(key), eventDateTimes);
    }

    public ArrayList<DateTime> getEventDateTimes(String key) {
        ArrayList<DateTime> eventDateTimes = get(getCacheKey(key));
        // Do stuff with eventDateTimes.
        return eventDateTimes;
    }
}

我尝试测试以确保CacheClientImplementation在设置和获取之前对提供给它的值执行特定的操作。我尝试通过劫持get()set()方法来模拟redis缓存本身,以便在测试中检查“缓存”的内容。

@RunWith(MockitoJUnitRunner.class)
public class CacheClientImplementationTest{
    @Mock
    private RedissonClient redissonClient;

    @Mock
    private RedisKeyGenerator redisKeyGenerator;

    @Spy
    @InjectMocks
    private CacheClientImplementation cacheClient = new CacheClientImplementation();

    private final HashMap<String, ArrayList<DateTime>> cacheMap = new HashMap<>();

    @Before
    public void setup() {
        Mockito.doAnswer((ver) -> {
            cacheMap.put(ver.getArgumentAt(0, Object.class).toString(), ver.getArgumentAt(1, ArrayList.class));
            return null;
        }).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));
        Mockito.doAnswer((ver) -> cacheMap.getOrDefault(ver.getArgumentAt(0, Object.class).toString(), null))
            .when(cacheClient).get(Mockito.any());
    }

    @After
    public void teardown() {
        cacheMap.clear();
    }
}

但是,当我在文件中运行测试时,我最终遇到了这个问题。

C:\...\CacheClientImplementationTest.java:20: error: SimpleCacheClient.SimpleCacheKey has protected access in SimpleCacheClient
        }).when(cacheClient).set(Mockito.any(), Mockito.any(ArrayList.class));

有没有什么方法可以在不改变SimpleCacheKey的情况下对这些方法执行doAnswer
谢谢你!

cpjpxq1n

cpjpxq1n1#

这归结为SimpleCacheKey类的可见性,你不能从不同的包中使用它,所以Mockito.any()不能使用该类作为返回类型,除非单元测试和SimpleCacheClient在同一个包中。
一种解决方案是将单元测试移到SimpleCacheClient所在的包中。如果这是从一个不同的库中加载的,而您无法更改它,那么您可以重新创建相同的包结构来欺骗编译器,使其认为该包是相同的,从而允许您访问protected类。但我相信这种技巧在Java 9模块中不起作用。
一个更好的解决方案是对CacheClientImplementation和单元测试做一个小的修改;把你无法影响的部分封装起来并嘲笑它。
由于您实际上并不关心SimpleCacheKey,而只关心String key,因此以下内容应该符合您的意图:

public class CacheClientImplementation extends SimpleCacheClient<ArrayList<DateTime>> {
    public void addEventDateTimes(String key, ArrayList<DateTime> eventDateTimes) {
        // Do stuff with eventDateTimes and then
        setForKey(key, eventDateTimes);
    }

    public ArrayList<DateTime> getEventDateTimes(String key) {
        ArrayList<DateTime> eventDateTimes = getForKey(key);
        // Do stuff with eventDateTimes.
        return eventDateTimes;
    }

    protected ArrayList<DateTime> getForKey(String key) {
        return super.get(getCacheKey(key));
    }

    protected void setForKey(String key, ArrayList<DateTime> value) {
        super.set(getCacheKey(key), value);
    }
}

在单元测试中,您将重写为我们刚刚创建的forKey变体:

Mockito.doAnswer(myAnswer1()).when(cacheClient).setForKey(Mockito.any(), Mockito.any(ArrayList.class));
Mockito.doAnswer(myAnswer2()).when(cacheClient).getForKey(Mockito.any());

我已经创建了新的方法protected,以便不会混淆调用者使用哪个方法,所以在本例中,单元测试必须与CacheClientImplementation在同一个(测试)包中。

相关问题