2

在 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";
    }


  }
}

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

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

4

3 回答 3

2

我解决了!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 的钩子并改为使用它。

于 2013-08-09T21:59:42.690 回答
0

哦,保持简单!你的测试班可以有

  • 一个私有方法,它实际运行测试,它的参数对应于你想要从一个测试更改为下一个测试的各种内容;
  • 几个公共的测试方法,每个只调用私有方法,参数值不同。
于 2013-08-09T04:13:25.797 回答
0

替代解决方案怎么样?

1.使用模板模式抽取一个抽象测试类,使初始条件准备成为一个抽象方法。

2.每个测试用例扩展模板并覆盖初始条件准备的实现。

3.将它们全部分组到一个测试套件中。

于 2013-08-09T00:51:47.000 回答