如何模拟具有私有构造函数参数的方法?
How can I mock a method that has a parameter with a private constructor?
我有一个抽象缓存客户端,它有一个我正在尝试向其添加单元测试的实现,并且它有一个受保护的 class 密钥实现。像这样:
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 缓存本身来读取和写入 from/to a HashMap
以便我可以检查“缓存”在我的测试中。
@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
这些方法吗?
谢谢!
这归结为 SimpleCacheKey
class 的可见性,您根本无法从不同的包中使用它。因此 Mockito.any()
不能将 class 用作 return 类型,除非单元测试与 SimpleCacheClient
.
在同一个包中
一个解决方案是将单元测试移动到与 SimpleCacheClient
相同的包中。如果这是从您无法更改的不同库加载的,您可以 re-create 相同的包结构来欺骗编译器认为包是相同的,让您可以访问 protected
class是的。
但我相信这个技巧不适用于 Java 9 个模块。
更好的解决方案是对 CacheClientImplementation
和单元测试进行小的修改;封装你无法影响的部分,mock那部分。
由于您并不真正关心 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
在相同的(测试)包中。
我有一个抽象缓存客户端,它有一个我正在尝试向其添加单元测试的实现,并且它有一个受保护的 class 密钥实现。像这样:
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 缓存本身来读取和写入 from/to a HashMap
以便我可以检查“缓存”在我的测试中。
@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
这些方法吗?
谢谢!
这归结为 SimpleCacheKey
class 的可见性,您根本无法从不同的包中使用它。因此 Mockito.any()
不能将 class 用作 return 类型,除非单元测试与 SimpleCacheClient
.
一个解决方案是将单元测试移动到与 SimpleCacheClient
相同的包中。如果这是从您无法更改的不同库加载的,您可以 re-create 相同的包结构来欺骗编译器认为包是相同的,让您可以访问 protected
class是的。
但我相信这个技巧不适用于 Java 9 个模块。
更好的解决方案是对 CacheClientImplementation
和单元测试进行小的修改;封装你无法影响的部分,mock那部分。
由于您并不真正关心 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
在相同的(测试)包中。