如何确定代码是否在JUnit测试内运行?


75

在我的代码中,仅当它在JUnit测试中运行时,才需要进行某些修复。如何确定代码是否在JUnit测试内运行?是否有类似JUnit.isRunning()== true的东西?


1
如果您的代码对测试做了不同的事情,那么您确实违反了TDD的想法。为什么只需要在测试内进行更改?
ctrlShiftBryan

17
这是一个糟糕的主意
KitsuneYMG '02

2
FWIW,我经常发现在开发时这样做很有用。将jUnit的检查放在API的最外层方法中,当在容器中运行时会在最后抛出运行时异常。这样,您可以运行单元测试并获得有效的结果,还可以从UI轻松测试整个堆栈,但是由于RTE的原因,您的事务会被丢弃。
Sophistifunk

15
我认为我有一个非常有效的用例:如果发生严重错误,我想在生产环境中发送电子邮件,如果在测试用例(开发人员而不是生产人员)中,我不希望该电子邮件。支持人员,将自己看到失败的结果)。我们有一百多个JUnit测试,我不想修改每个测试以调用类似EmailService.dontSendEmailsToProductionTeam()的东西
Ryan Shillington

3
我想知道代码是否在单元测试中运行,所以我可以做一些事情,例如告诉它使用不同的S3存储桶。这样,我仍然可以测试所有内容,而不必完全模拟出S3。
楚格沃特2014年

Answers:


71

如果要以编程方式决定要运行哪个“配置文件”,则可能是一个好主意。考虑使用Spring Profiles进行配置。在集成测试中,您可能需要针对其他数据库进行测试。

这是经过测试的代码

public static boolean isJUnitTest() {  
  for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
    if (element.getClassName().startsWith("org.junit.")) {
      return true;
    }           
  }
  return false;
}


    

2
对于JUnit测试,我们需要对数据库环境进行不同的配置。
Audrius Meskauskas

此解决方案都不需要将junit置于生产代码的类路径中!完善!
Italo Borssatto

我已经编写了一个可以永远运行的服务-我希望它在进行单元测试时经过一次迭代后退出。太好了,谢谢。
戴夫

2
for(StackTraceElement element:currentThread()。getStackTrace()){
Mike

28

首先,这可能不是一个好主意。您应该对实际的生产代码进行单元测试,而不要稍有不同。

如果确实要执行此操作,则可以查看stacktrace,但是由于无论如何都要为此更改程序,因此最好isUnitTesting在代码中引入一个新的静态布尔字段,并将JUnit设置为true。把事情简单化。


是的,例如,您可能在类中包含isUnitTesting字段的单例对象,然后在JUnit @Before方法中将其设置为true。
Jim Ferrans 2010年

5
想起来,这有点像依赖注入。他应该为难以测试的依赖项注入模拟对象,但是我认为将布尔值显式设置为true还是比让类自行解决要好。
Thilo 2010年

1
伙计们,您说服并鼓励我改写这个问题。stackoverflow.com/questions/2351293/...
马克斯Völkel博士

“您应该对实际的生产代码进行单元测试”。没有人这样做。单元测试几乎总是使用某种形式的模拟,通常通过多态性(即接口)来实现。使用标志只是做同一件事的另一种方法。两者之间的差异是风格上的,相对而言是微不足道的。
好战的黑猩猩

18

这个线程上的许多人说,在JUnit下运行稍微不同的代码是一个坏主意。我普遍同意,但我认为也有一些例外。

例如,我目前正在为连接到数据库的应用程序编写INTEGRATION(相对于Unit)测试。

这些验收测试通常需要使用特定的测试数据完全重新初始化数据库。

显然,我不希望在实际的生产数据库上进行此操作,因为这可能会完全删除有价值的生产数据。

确保这种情况永远不会发生的最简单方法是,使代码无法在JUnit下运行时连接到生产数据库。反过来,例如,如果生成连接的Factory可以告诉它正在JUnit下运行,并且在这种情况下,除非我们尝试连接的数据库具有已知的名称,它将返回空连接,则可以执行此操作成为测试数据库(例如:“ testdatabase”)。


4
通过使您的代码不了解生产数据库可以解决此问题。它将使用环境变量或特定位置中的任何配置。生产配置可能是一个不为人知的配置(即仅存在于部署中,而不存在于版本控制中)。这不仅更安全,而且还解决了测试问题,并且不需要修改生产代码。
TWiStErRob

11

我可以找到一个理由,那就是当您想在生产类中提供代码以帮助测试时。这将类似于Java断言,仅在设置了调试标志时才适用。

像这样:

Object debugState() {
    // This is only meant to be called when testing
    if (!JUnit.isRunning()) {
        throw new IllegalStateException("Not in a test!");
    }
    // Now compute possibly costly debug information
    // not to be used in production
    Object state = ...
}

5
这证明了问题的
正确性

6

如果您由于进行单元测试而做事有所不同,那么您就无法达到单元测试的目的。单元测试应该表现得与生产完全相同(除了设置和拆卸任何必要的数据以及您需要的数据……,但这包含在JUnit测试本身中,而不是您的代码中)。

但是,如果您确实有充分的理由这样做,那么我将看一下堆栈,看看是否有JUnit。


我知道这听起来有点反对TDD,但是使用Jersey / Jetty的GoogleAppengine测试代码在我的本地计算机上引入了多个线程,这又导致新线程初始化时没有正确的Google模拟测试环境,因此我需要检查我的潜在第二个线程的许多点(如果我的AppEngine模拟环境已经正确初始化),如果我在生产服务器中,则不想这样做。我显然有点混乱,如果可以在本地计算机上更紧密地模仿AppEngine环境(即只有一个线程),那就更好了。
MaxVölkel博士

1
我认为这应该是您的实际问题。如何使单元测试的行为与部署的环境相同。这可能会帮助您真正解决问题,而无需引入代码来实际检查您是否正在运行单元测试。
罗宾

为什么不使用GAE开发服务器测试GAE?这与真实情况非常接近,我相信他们现在甚至都支持JUnit。
Thilo 2010年

对于GAE:if(SystemProperty.environment.value()== null){//在单元测试中}
cputoaster


2

使用Spring时,可以定义一个保存该值的bean。

在应用程序上下文中:

@Bean
public boolean isRunningTests() {
    return false;
}

在测试应用程序上下文中:

@Bean
public boolean isRunningTests() {
    return true;
}

将值注入到Spring组件中:

@Resource
private boolean isRunningTests;

private void someMethod() {
    if (isRunningTests()) {
        ....

2

这对我有用:

private Boolean isRunningTest = null;

private boolean isRunningTest() {
    if (isRunningTest == null) {
        isRunningTest = true;
        try {
            Class.forName("org.junit.Test");
        } catch (ClassNotFoundException e) {
            isRunningTest = false;
        }
    }
    return isRunningTest;
}

只要将junit作为范围“ test”的依赖项包括在内,它就可以在Maven测试以及Eclipse IDE中使用。例如:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>

0

为了区分测试和不测试,您始终可以在application-test.properties中定义特殊属性或值。


0

最短的(最少的代码)解决方案是在测试运行时设置一个全局标志。然后,您可以在main()(或类似的入口点)设置一次,而不必在所有测试类的设置方法中重复设置。只要没有其他输入代码的方法(没有替代方法main),它就可以工作。

第二个最短的解决方案是像Janning的answer一样扫描调用堆栈中的junit软件包。只要junit不会将自己隐藏在另一个库和一个执行程序的后面,这将起作用。

依赖注入也将起作用,但是在这种情况下,这只是设置本质上是全局标志的一种好方法。


-1

这与用户问题并不严格相关,但是我很确定到达那里的人可能会觉得有用。

我使用以下方法:公开将在测试中使用的程序包本地构造函数。

例如

src / main / java / ApplicationService.java

public class ApplicationService {
  private final Integer someInternalObject;

  public ApplicationService(String somePublicArgument) throws NumberFormatException {
    someInternalObject = Integer.valueOf(somePublicArgument, 16);
  }

  ApplicationService(Integer someInternalObject) {
     this.someInternalObject = someInternalObject;
  }
}

src / test / ApplicationServiceTest.Java

public class ApplicationServiceTest {
  @Test
  public void testSomething() throws Exception {
    ApplicationService applicationService = new ApplicationService(0);
  }
}

暴露给所有测试

不是期末班

在测试中对其进行扩展,并为包本地或受保护的包提供公共构造函数。

期末课程

在将使用程序包本地构造函数创建该程序包的同一程序包中(在测试中)创建一个公共工厂方法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.