Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 根据类路径上是否存在类来测试代码_Java_Unit Testing_Maven_Classpath - Fatal编程技术网

Java 根据类路径上是否存在类来测试代码

Java 根据类路径上是否存在类来测试代码,java,unit-testing,maven,classpath,Java,Unit Testing,Maven,Classpath,我有一个项目,如果SLF4J在类路径上,我需要提供SLF4J日志记录,否则直接提供到控制台的日志记录。我使用类似以下代码实例化我的记录器: try { Class.forName("org.slf4j.LoggerFactory"); return new Slf4JLogProvider(); } catch (ClassNotFoundException e) { System.out.println("SLF4J not on classpath, defaulti

我有一个项目,如果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不存在,并验证控制台日志已初始化

有什么干净的方法可以做到这一点吗?是否有任何框架可以提供对类路径相关测试的支持?在特定的类加载器中隔离测试的任何标准方法

正如@piotrek所指出的,这可能使集成测试多于单元测试。

extract Class.forNameorg.slf4j.LoggerFactory;在测试中分离服务并模拟它。您将有一行未覆盖

另一种方法是使用不同的类路径运行一些测试。不幸的是,在maven或gradle中没有很好的支持。然而,使用ant/ivy很容易做到


ps:您确定可以声明新的Slf4JLogProvider;在你的类中,如果它不在类路径上?类声明不是依赖于jvm吗?

您可以使用自定义类加载器来实现这一点。Class.forName调用类加载器,该类加载器加载了包含Class.forName调用的类。因此,当您的被测类尝试加载SLF4J时,它将调用加载被测类的同一个类加载器。您可以编写一个自定义类加载器,并使用该加载器加载被测类的副本。该类的副本将使用您的自定义类加载器加载其他类,这使您有机会隐藏您不希望被测试的类访问的类

您的自定义类加载器将成为现有类加载器类classloader或URLClassLoader的子类。您将添加以下行为:

当请求加载一个要隐藏的类时,加载程序会假装找不到该类。 当请求加载被测试的类时,直接从存储该类的位置加载该类。这将为您提供将使用此类加载器的类的副本。 对其他类java.lang.String等的请求被传递给父类加载器。 使用此自定义类加载器加载被测类的副本。生成的类对象和从类对象创建的实例将调用自定义加载程序来加载其他类。当被测试的类尝试加载SLF4J时,您的自定义加载程序将充当该类不存在的角色

编写一个代码示例有点复杂,但这里有一些链接说明了如何编写自定义类装入器


Slf4JLogProvider是一个自定义类,不是SLF4J的一部分。但是我希望我的测试能够捕捉到同样的错误。我们可以讨论这是否使它成为一个集成测试而不是一个单元测试……Class.forName已经由实现您的vm的人员进行了测试。如果你真的想测试这个调用,你必须用两个不同的类路径运行你的测试。问题是不能测试这个类。forName可以工作。问题在于测试如果我用作标记的类不存在,我可以正确加载回退类,并且我没有对我没有预料到的内容的隐藏依赖。如果该行位于另一个服务中,则模拟该服务,并使模拟抛出与class.forName抛出的异常相同的异常。然后,您将测试您的回退代码,这将是一个单元测试。这里需要的是集成或系统测试。问题不在于Class.forName会抛出异常,或者在特殊情况下有特殊行为。目标是验证代码的其余部分是否也能正常工作。例如,假设我的ConsoleLogProvider依赖于org.slf4j.Marker,模仿Class.forName抛出ClassNotFoundException将起作用,ConsoleLogProvider将被加载,测试将为绿色。但事实上,实现被破坏了,因为我对SLF4J有一个隐藏的依赖。加载的类不是缓存的吗?他仍然需要运行两次测试/使用两个不同的类路径,对吗?或者至少防止在第一次测试期间加载类使用正常的类加载机制,每个类加载器持有对其加载的类的引用。所以类倾向于留在内存中并被重用,是的。但是您可以在普通类加载器层次结构之外创建自己的类加载器,并使用它来加载类。如果删除对类加载器、从中加载的类以及从这些类创建的对象的引用,则所有内容都将被垃圾收集。您可以创建加载程序的另一个实例,然后再次执行整个操作。这就是tomcat等处理web应用程序热部署/取消部署的方式 ping已经有人编写了一些实用程序来实现这一点。测试代码与功能代码的比例在这一点上看起来并不合适-