测试代码取决于 class 路径上是否存在 class
Testing code dependant on existence of class on classpath
我有一个项目,如果 SLF4J 在类路径上,我需要提供 SLF4J 日志记录,否则直接向控制台提供日志记录。我用类似于以下的代码实例化我的记录器:
try {
Class.forName("org.slf4j.LoggerFactory");
return new Slf4JLogProvider();
} catch (ClassNotFoundException e) {
System.out.println("SLF4J not on classpath, defaulting to console logging");
}
return new ConsoleLogProvider();
请注意,Slf4JLogProvider
是 SLF4J 的自定义包装器,它不是 SLF4J 本身的一部分。
我的项目是基于 Maven 的,这个模块声明了对 SLF4J 的可选依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<optional>true</optional>
</dependency>
我希望能够测试这段代码。类路径很棘手,正确测试它很重要。基本上,我希望能够在我的测试期间修改类加载器以确保 SLF4J 不存在并验证控制台日志记录是否已初始化。
有什么"clean"方法可以做到这一点吗?任何支持类路径相关测试的框架?在特定类加载器中隔离测试的任何标准方法?
正如@piotrek 所指出的,这可能使它成为一个集成测试而不是单元测试。
提取 Class.forName("org.slf4j.LoggerFactory");
以分离服务并在您的测试中模拟它。你会发现一根线
另一种方法是 运行 使用不同的 class 路径进行一些测试。不幸的是,在 maven 或 gradle 中没有对它的良好支持。然而 ant/ivy 很容易做到
ps:如果 class 不在 class 路径中,您确定可以在 class 中声明 new Slf4JLogProvider();
吗? class 声明不依赖于 jvm 吗?
您可以使用自定义 classloader 来完成此操作。 Class.forName()
调用 classloader 加载包含 Class.forName() 调用的 class。因此,当您的 class under test 尝试加载 SLF4J 时,它将调用相同的 classloader 来加载 class under test。您可以编写自定义 classloader 并使用该加载程序加载被测 class 的副本。 class 的副本将使用您的自定义 classloader 加载其他 classes,这使您有机会隐藏您不想要的 classes class正在测试访问中。
您的自定义 classloader 将子class 现有 classloader class 之一(ClassLoader 或 URLClassLoader)。您将添加此行为:
- 当请求加载您想要隐藏的 class 之一时,加载程序会假装未找到 class。
- 当请求加载被测class时,直接从存储的地方加载class。这为您提供了 class 的副本,它将使用此 classloader.
- 其他 classes(java.lang.String 等)的请求被传递给父 classloader。
使用此自定义 classloader 加载被测 class 的副本。生成的 class 对象和从 class 对象创建的实例将调用您的自定义加载程序来加载其他 classes。当测试中的 class 尝试加载 SLF4J 时,您的自定义加载器将表现得好像 class 不存在一样。
编写代码示例有点复杂,但这里有一些链接说明了如何编写自定义 class 加载程序。
我有一个项目,如果 SLF4J 在类路径上,我需要提供 SLF4J 日志记录,否则直接向控制台提供日志记录。我用类似于以下的代码实例化我的记录器:
try {
Class.forName("org.slf4j.LoggerFactory");
return new Slf4JLogProvider();
} catch (ClassNotFoundException e) {
System.out.println("SLF4J not on classpath, defaulting to console logging");
}
return new ConsoleLogProvider();
请注意,Slf4JLogProvider
是 SLF4J 的自定义包装器,它不是 SLF4J 本身的一部分。
我的项目是基于 Maven 的,这个模块声明了对 SLF4J 的可选依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<optional>true</optional>
</dependency>
我希望能够测试这段代码。类路径很棘手,正确测试它很重要。基本上,我希望能够在我的测试期间修改类加载器以确保 SLF4J 不存在并验证控制台日志记录是否已初始化。
有什么"clean"方法可以做到这一点吗?任何支持类路径相关测试的框架?在特定类加载器中隔离测试的任何标准方法?
正如@piotrek 所指出的,这可能使它成为一个集成测试而不是单元测试。
提取 Class.forName("org.slf4j.LoggerFactory");
以分离服务并在您的测试中模拟它。你会发现一根线
另一种方法是 运行 使用不同的 class 路径进行一些测试。不幸的是,在 maven 或 gradle 中没有对它的良好支持。然而 ant/ivy 很容易做到
ps:如果 class 不在 class 路径中,您确定可以在 class 中声明 new Slf4JLogProvider();
吗? class 声明不依赖于 jvm 吗?
您可以使用自定义 classloader 来完成此操作。 Class.forName()
调用 classloader 加载包含 Class.forName() 调用的 class。因此,当您的 class under test 尝试加载 SLF4J 时,它将调用相同的 classloader 来加载 class under test。您可以编写自定义 classloader 并使用该加载程序加载被测 class 的副本。 class 的副本将使用您的自定义 classloader 加载其他 classes,这使您有机会隐藏您不想要的 classes class正在测试访问中。
您的自定义 classloader 将子class 现有 classloader class 之一(ClassLoader 或 URLClassLoader)。您将添加此行为:
- 当请求加载您想要隐藏的 class 之一时,加载程序会假装未找到 class。
- 当请求加载被测class时,直接从存储的地方加载class。这为您提供了 class 的副本,它将使用此 classloader.
- 其他 classes(java.lang.String 等)的请求被传递给父 classloader。
使用此自定义 classloader 加载被测 class 的副本。生成的 class 对象和从 class 对象创建的实例将调用您的自定义加载程序来加载其他 classes。当测试中的 class 尝试加载 SLF4J 时,您的自定义加载器将表现得好像 class 不存在一样。
编写代码示例有点复杂,但这里有一些链接说明了如何编写自定义 class 加载程序。