- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试对调用类“B”的静态方法的类“A”进行单元测试。 “B”类本质上有一个 google guava 缓存,它在给定键的情况下从缓存中检索值(对象),或者使用服务适配器将对象加载到缓存中(以防缓存未命中)。服务适配器类又具有其他 Autowiring 的依赖项来检索对象。
这些是用于说明目的的类:
A 级
public class A {
public Object getCachedObject(String key) {
return B.getObjectFromCache(key);
}
}
B 级
public class B {
private ServiceAdapter serviceAdapter;
public void setServiceAdapter(ServiceAdapter serAdapt) {
serviceAdapter = serAdapt;
}
private static final LoadingCache<String, Object> CACHE = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(30, TimeUnit.MINUTES)
.build(new MyCacheLoader());
public static Object getObjectFromCache(final String key) throws ExecutionException {
return CACHE.get(warehouseId);
}
private static class MyCacheLoader extends CacheLoader<String, Object> {
@Override
public Object load(final String key) throws Exception {
return serviceAdapter.getFromService(key)
}
}
}
服务适配器类
public class ServiceAdapter {
@Autowired
private MainService mainService
public Object getFromService(String key) {
return mainService.getTheObject(key);
}
}
我能够成功进行集成测试并从缓存中获取(或加载)值(或加载到缓存中)。但是,我无法为 A 类编写单元测试。这是我尝试过的:
A 类单元测试
@RunWith(EasyMocker.class)
public class ATest {
private final static String key = "abc";
@TestSubject
private A classUnderTest = new A();
@Test
public void getCachedObject_Success() throws Exception {
B.setServiceAdapter(new ServiceAdapter());
Object expectedResponse = createExpectedResponse(); //some private method
expect(B.getObjectFromCache(key)).andReturn(expectedResponse).once();
Object actualResponse = classUnderTest.getCachedObject(key);
assertEquals(expectedResponse, actualResponse);
}
}
当我运行单元测试时,它失败并在调用 mainService.getTheObject(key) 的 ServiceAdapter 类出现 NullPointerException。
我如何在单元测试类 A 时模拟 ServiceAdapter 的依赖性。我不应该只关心类 A 具有的直接依赖性,即。 B、
我确定我做的事情从根本上是错误的。 A类的单元测试应该怎么写?
最佳答案
你现在知道为什么静态方法被认为是单元测试的坏习惯了,因为他们几乎不可能 mock ,特别是。如果它们是有状态的。
因此,将 B static
方法重构为一组非静态公共(public)方法更为实用。
A 类应该通过构造函数或 setter 注入(inject)来注入(inject) B 类的实例。在你的 ATest 中,你然后用类 B 的模拟实例化类 A,并让它根据你的测试用例返回你喜欢的任何东西,并以此为基础你的断言。
这样做你真正测试了 unit,它最终应该是类 A 的公共(public)接口(interface)。(这也是为什么我喜欢一个类在一个类中只有一个公共(public)方法的原因理想世界。)
关于您的具体示例:B 的模拟也不应该关心它自己的依赖项。你目前在你的测试中写:
B.setServiceAdapter(new ServiceAdapter());
您在 ATest
中。不在 BTest
中。 ATest
应该只有 B
的 mock,因此不需要传递 ServiceAdapter
的实例。
您只需要关心 A 的公共(public)方法的行为方式,这可能会根据 B 的公共(public)方法的某些响应而改变。
我还觉得奇怪的是,您想要测试的方法基本上只是 B 的包装器。也许这对您的情况有意义,但这也暗示我您可能想要注入(inject)一个 Object 而不是 B 的实例。
如果你不想迷失在模拟 hell 中,那么每个类的公共(public)方法越少越好,这反过来又尽可能减少依赖性。我争取每个类(class)有 3 个依赖关系,在特殊情况下允许最多 5 个依赖关系。 (每个依赖项都可能对模拟开销产生巨大影响。)
如果您有太多依赖项,当然可以将某些部分移至其他/新服务。
关于java - 对调用静态方法的类进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39922637/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!