java - 运行同一类但具有不同初始条件的测试套件

标签 java unit-testing junit integration-testing junit4

在 JUnit 4 中,我希望编写一个测试套件,该套件由同一测试用例的多种风格组成,只是每个测试用例具有不同的初始条件。这是一个例子:

import java.io.File;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({MultiInputClientServerIntegrationTest.NormalInput.class,
    MultiInputClientServerIntegrationTest.SimulationHashIssue.class})
public class MultiInputClientServerIntegrationTest {


  @RunWith(Suite.class)
  @SuiteClasses({TestClientServerIntegration.class})
  public class NormalInput {}




  @RunWith(Suite.class)
  @SuiteClasses({TestClientServerIntegration.class})
  public class SimulationHashIssue {


    public SimulationHashIssue() {
      TestClientServerIntegration.simulation = new File("test\\BEECHA01\\sim2.zip");
      TestClientServerIntegration.inputFile = "files\\config.in";
    }


  }
}

如您所见,两个内部类都有 TestClientServerIntegration.class 的 SuiteClass,但第二个内部类正在更改一些静态变量值。我发现这个构造函数永远不会被调用,所以这些静态变量永远不会改变。

我的最终目标是使用多种类型的输入一遍又一遍地运行这个TestClientServerIntegration.class。如果我能以这种方式运行测试套件,那就太理想了——所以希望这是可能的。我希望尽可能少地修改 JUnit,但是需要完成的事情就会完成。

最佳答案

我解决了! 《JUnit in action》一书有很大帮助。这是我的代码:

/**
 * The Class MultiInputClientServerIntegrationTest.
 */
@RunWith(Suite.class)
@SuiteClasses({MultiInputClientServerIntegrationTest.NormalInput.class,
    MultiInputClientServerIntegrationTest.BEECHA01SimulationHashIssue.class})
public class MultiInputClientServerIntegrationTest {

  /**
   * The Class NormalInput.
   */
  @RunWith(Suite.class)
  @SuiteClasses({TestClientServerIntegration.class})
  public class NormalInput {}

  /**
   * The Class BEECHA01SimulationHashIssue.
   */
  // @RunWith(Suite.class)
  @RunWith(InterceptorRunner.class)
  @SuiteClasses({TestClientServerIntegration.class})
  @InterceptorClasses({BEECHA01SimulationHashIssueInterceptor.class})
  public static class BEECHA01SimulationHashIssue extends TestClientServerIntegration {  }

  /**
   * The Class BEECHA01SimulationHashIssueInterceptor.
   */
  public static class BEECHA01SimulationHashIssueInterceptor implements Interceptor {
    static File sim = new File("test\\BEECHA01\\6dof_Block20_FD2_2.zip");
    static String in = "BEECHA01\\config.in";

    /*
     * (non-Javadoc)
     * 
     * @see test.northgrum.globalhawk.simulation.Interceptor#interceptBefore()
     */
    @Override
    public void interceptBefore() {
      if (!TestClientServerIntegration.simulation.equals(sim)
          || !TestClientServerIntegration.inputFile.equals(in)) {
        TestClientServerIntegration.simulation = sim;
        TestClientServerIntegration.inputFile = in;
        System.out.println("Test set up with BEECHA01 Initial Parameters");
      }
    }

    /*
     * (non-Javadoc)
     * 
     * @see test.northgrum.globalhawk.simulation.Interceptor#interceptAfter()
     */
    @Override
    public void interceptAfter() {}
  }
}

特殊运行者在哪里:

/**
 * This interface is used to declare the methods for every interceptor.
 * 
 * @version $Id: Interceptor.java 201 2009-02-15 19:18:09Z paranoid12 $
 */
public interface Interceptor {
  /**
   * This method will be called before every test - we can implement our own logic in every
   * implementation.
   */
  public void interceptBefore();

  /**
   * This method will be called after every test - we can implement our own logic in every
   * implementation.
   */
  public void interceptAfter();
}

并且,

/**
 * A custom runner for JUnit4.5 in which we demonstrate the interceptor pattern.
 * 
 * @version $Id: InterceptorRunner.java 201 2009-02-15 19:18:09Z paranoid12 $
 */
public class InterceptorRunner extends BlockJUnit4ClassRunner {
  /**
   * This is the InterceptorClasses annotation, which serves to hold our interceptor class
   * implementations.
   */
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.TYPE)
  public @interface InterceptorClasses {

    /**
     * Value.
     * 
     * @return the classes to be run
     */
    public Class<?>[] value();
  }

  /**
   * This constructor is a must.
   * 
   * @param clazz the test-case class
   * @throws InitializationError the initialization error
   */
  public InterceptorRunner(Class<?> clazz) throws InitializationError {
    super(clazz);
  }

  /**
   * Override the methodInvoker, so that when it is called we wrap the statement with our own.
   * 
   * @param method the test method
   * @param test the test-case
   * @return the statement
   */
  @Override
  public Statement methodInvoker(FrameworkMethod method, Object test) {
    InterceptorStatement statement = new InterceptorStatement(super.methodInvoker(method, test));
    InterceptorClasses annotation = test.getClass().getAnnotation(InterceptorClasses.class);
    Class<?>[] klasez = annotation.value();
    try {
      for (Class<?> klaz : klasez) {

        statement.addInterceptor((Interceptor) klaz.newInstance());

      }
    } catch (IllegalAccessException ilex) {
      ilex.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    }
    return statement;
  }
}

/**
 * A statement for our custom runner.
 * 
 * @version $Id: InterceptorStatement.java 201 2009-02-15 19:18:09Z paranoid12 $
 */
public class InterceptorStatement extends Statement {
  /**
   * A wrapping invoker that will procede the execution, once we execute our interceptors.
   */
  private final Statement invoker;

  /**
   * A list of interceptors that will be executed before the other statements.
   */
  private List<Interceptor> interceptors = new ArrayList<Interceptor>();

  /**
   * A constructor that takes another invoker to wrap our statement.
   * 
   * @param invoker the invoker
   */
  public InterceptorStatement(Statement invoker) {
    this.invoker = invoker;
  }

  /**
   * We override this method to call our interceptors, and then evaluate the wrapping invoker.
   * 
   * @throws Throwable the throwable
   */
  @Override
  public void evaluate() throws Throwable {
    for (Interceptor interceptor : interceptors) {
      interceptor.interceptBefore();
    }

    invoker.evaluate();

    for (Interceptor interceptor : interceptors) {
      interceptor.interceptAfter();
    }
  }

  /**
   * Add another interceptor to the list of interceptors we have.
   * 
   * @param interceptor we want to add
   */
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
}

真正有帮助的是切换到 JUnit 4.10,因为它提供了更详细的错误消息。无论如何,这里的主要区别是我让我的“自定义输入”测试扩展了实际测试。然后我创建了一个拦截器,它重载了 @Before 和 @After 方法,并且可以在每个单独的 @Test 之前更改参数。

事实上,我更喜欢只重载每个 @BeforeClass 的东西,但乞丐不是选择者。这已经足够好了并且可以正确完成工作。它与 Eclipse 一起工作。希望我能遇到 @BeforeClass 的钩子(Hook)并改为使用它。

关于java - 运行同一类但具有不同初始条件的测试套件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18136095/

相关文章:

java - 代码不抛出异常

java - 如何从 setOnItemSelectedListener 方法获取字符串?

python - 如何将命令行参数传递给在 vscode 中运行的 pytest 测试

java - Spring实战第2章把XML bean改成JavaConfig过不了关

java - 如何使 IntelliJ/Android Studio 中的静态导入更快

javascript - Angular通过调用URL在浏览器中获取CSV文件

java - 接口(interface)能否以某种方式阻止 lambda 表达式的实现?

c# - 单元测试 Prism 5 async Delegatecommand 并行执行

Android:在 ListView 中获取第 i 个 TextView

Java:测试抛出已检查异常