【问题标题】:Running a Test Suite of the same class but with different initial conditions运行相同类但具有不同初始条件的测试套件
【发布时间】:2013-08-10 18:14:52
【问题描述】:

在 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 的 SuiteClasses,但第二个是更改一些静态变量值。我发现这个构造函数永远不会被调用,所以这些静态数据永远不会改变。

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

【问题讨论】:

    标签: java unit-testing junit integration-testing junit4


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      替代解决方案怎么样?

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

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

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

      【讨论】:

      • 1 - 有这方面的例子吗?我以为我是这样做的 2 - 有什么例子吗? 3 - 这是我在这里尝试做的,但我似乎无法指定初始条件。
      【解决方案3】:

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

      • 一种私有方法,它实际运行测试,它的参数对应于您希望从一个测试更改为下一个测试的各种内容;
      • 几个公共测试方法,每个只调用私有方法,参数值不同。

      【讨论】:

      • 任何例子...?您意识到我想一遍又一遍地重新运行相同的测试,我认为这意味着我需要重载套件或跑步者。
      • 不,您想一遍又一遍地运行相同的几行代码,但在某些变量中具有不同的值。这意味着您不需要超载 Suite、Runners 或任何类似的东西。但不要让我阻止你让它变得比需要的复杂得多。
      • 我想我明白你的意思——创建我自己的 JUnitCore 实例并从那里运行测试。我不知道这是否适用于 eclispe JUnit 测试应用程序...
      • 不,我完全不是这个意思。只需编写一个完全普通的 JUnit 测试类,包含许多公共测试方法,但将公共部分重构为私有方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-21
      相关资源
      最近更新 更多