【问题标题】:How can I use the Parameterized JUnit test runner with a field that's injected using Spring?如何将参数化 JUnit 测试运行器与使用 Spring 注入的字段一起使用?
【发布时间】:2011-03-25 05:47:40
【问题描述】:

我正在使用 Spring 将目录路径注入到我的单元测试中。在这个目录中,有一些文件应该用于使用Parameterized 测试运行程序为参数化测试用例生成测试数据。不幸的是,测试运行程序要求提供参数的方法是静态的。这不适用于我的情况,因为该目录只能注入到非静态字段中。有什么想法可以解决这个问题吗?

【问题讨论】:

  • 你能改用另一种机制来将字符串注入测试装置吗?我的意思是这里的瓶颈似乎是 Spring 无法设置非静态字段/
  • Gishu: 设置静态字段,不是吗?也许不在 JUnit 中,因为订单或执行将在 Spring 之前调用 @Parameters 方法。但总的来说呢?

标签: java spring junit


【解决方案1】:

您可以使用 Spring 中的 TestContextManager。在这个例子中,我使用的是理论而不是参数化。

@RunWith(Theories.class)
@ContextConfiguration(locations = "classpath:/spring-context.xml")
public class SeleniumCase {
  @DataPoints
  public static WebDriver[] drivers() {
    return new WebDriver[] { firefoxDriver, internetExplorerDriver };
  }

  private TestContextManager testContextManager;

  @Autowired
  SomethingDao dao;

  private static FirefoxDriver firefoxDriver = new FirefoxDriver();
  private static InternetExplorerDriver internetExplorerDriver = new InternetExplorerDriver();

  @AfterClass
  public static void tearDown() {
    firefoxDriver.close();
    internetExplorerDriver.close();
  }

  @Before
  public void setUpStringContext() throws Exception {
    testContextManager = new TestContextManager(getClass());
    testContextManager.prepareTestInstance(this);
  }

  @Theory
  public void testWork(WebDriver driver) {
    assertNotNull(driver);
    assertNotNull(dao);
  }
}

我在这里找到了这个解决方案:How to do Parameterized/Theories tests with Spring

【讨论】:

  • 说你需要用一个Spring注解来标记这个测试,比如@Transactional。您将如何使用 TestContextManager 做到这一点?
  • @gnuf 链接坏了
【解决方案2】:

我假设您使用的是 JUnit 4.X,因为您提到了参数化测试运行器。这意味着您没有使用@RunWith(SpringJUnit4ClassRunner)。没问题,只是列出我的假设。

下面使用Spring从XML文件中获取测试文件目录。它不会注入它,但数据仍然可供您的测试使用。并且在静态方法中也不少。

我看到的唯一缺点是它可能意味着您的 Spring 配置被多次解析/配置。如果需要,您可以只加载一个包含测试特定信息的较小文件。

@RunWith(Parameterized.class)
public class MyTest {
    @Parameters
    public static Collection<Object[]> data() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("/jeanne/jeanne.xml");
        String dir = ctx.getBean("testFilesDirectory", String.class);

            // write java code to link files in test directory to the array
        return Arrays.asList(new Object[][] { { 1 } });
    }
// rest of test class
}

【讨论】:

  • 好主意。我完全忘记了手动获取ApplicationContext。它使代码有点难看,但它使测试输出无限好,可以使用Parameterized
【解决方案3】:

对于 2015 年末或更晚阅读本文的人,Spring 4.2 除了 SpringJUnit4ClassRunner 还添加了 SpringClassRule 和 SpringMethodRule,它们利用了对 Spring TestContext Framework 的支持。

这意味着对任何 Runner 的一流支持,例如 MockitoJUnitRunnerParameterized

@RunWith(Parameterized.class)
public class FibonacciTest {
    @ClassRule public static final SpringClassRule SCR = new SpringClassRule();
    @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule();

    long input;
    long output;

    public FibonacciTest(long input, long output) { this.input = input; ...} 

    @Test
    public void testFibonacci() {
        Assert.assertEquals(output, fibonacci(input));
    }

    public List<Long[]> params() {
        return Arrays.asList(new Long[][] { {0, 0}, {1, 1} });
    }
}

【讨论】:

    【解决方案4】:

    使用@RunWith(Parameterized.class)@ContextConfiguration注释测试类就足够了,使用@Autowired进行依赖注入并在构造函数中使用TestContextManager进行初始化,例如:

    @RunWith(Parameterized.class)
    @ContextConfiguration(classes = TestConfig.class)
    public class MyTest {
    
        @Autowired
        private DataSource dataSource;
    
        private final int param;
    
        @Parameterized.Parameters
        public static List<Object[]> params() {
            return Arrays.asList(new Object[][]{
                {1},
                {2},
            });
        }
    
        public MyTest(int p) {
            this.param = p;
            new TestContextManager(getClass()).prepareTestInstance(this);
        }
    
        @Test
        public void testSomething() {
           …
        }
    }
    

    【讨论】:

    • 提个醒,记住你不能使用注入的bean(本例中的dataSource)作为参数源。 @Parameterized.Parameters 方法需要是静态的。
    • 你从哪里得到TestConfig 类?
    • TestConfig 是一个简单的 spring @Configuration 类,其中定义了 @Bean DataSource dataSource(){...} 方法。
    【解决方案5】:

    这是第一个没有 JUnit 4.12 参数化工厂的解决方案,下面是一个改进的解决方案。

    没有事务支持的静态上下文

    让 Spring 使用 TestContextManager 类进行所有配置解析和自动装配。

    诀窍是使用假测试实例来获取自动装配的字段并将它们传递给将有效运行的参数化测试。

    但请记住 prepareTestInstance() 进行自动装配,但不管理测试事务以及 beforeTestMethod()afterTestMethod() 处理的其他好东西。

    @RunWith(Parameterized.class)
    @ContextConfiguration(locations = {"/test-context.xml", "/mvc-context.xml"})
    @WebAppConfiguration
    @ActiveProfiles("test-profile")
    public class MyTest {
    
      @Parameters
      public static Collection<Object[]> params() throws Exception {
        final MyTest fakeInstance = new MyTest();
        final TestContextManager contextManager = new TestContextManager(MyTest.class);
        contextManager.prepareTestInstance(fakeInstance);
        final WebApplicationContext context = fakeInstance.context;
    
        // Do what you need with Spring context, you can even access web resources
        final Resource[] files = context.getResources("path/files");
        final List<Object[]> params = new ArrayList<>();
        for (Resource file : files) {
          params.add(new Object[] {file, context});
        }
        return params;
      }
    
      @Parameter
      public Resource file;
      @Autowired
      @Parameter(1)
      public WebApplicationContext context;
    }
    

    但是,如果您有很多自动装配的字段,则会出现一个缺点,因为您必须手动将它们传递给数组参数。

    完全支持 Spring 的参数化工厂

    JUnit 4.12 引入了ParametersRunnerFactory,它允许结合参数化测试和 Spring 注入。

    public class SpringParametersRunnerFactory implements ParametersRunnerFactory {
    @Override
      public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
        final BlockJUnit4ClassRunnerWithParameters runnerWithParameters = new BlockJUnit4ClassRunnerWithParameters(test);
        return new SpringJUnit4ClassRunner(test.getTestClass().getJavaClass()) {
          @Override
          protected Object createTest() throws Exception {
            final Object testInstance = runnerWithParameters.createTest();
            getTestContextManager().prepareTestInstance(testInstance);
            return testInstance;
          }
        };
      }
    }
    

    可以将工厂添加到以前的测试类中以提供完整的 Spring 支持,例如 test transactionreinit dirty contextservlet test。当然,不再需要将假测试实例中的自动装配字段传递给参数化测试。

    @UseParametersRunnerFactory(SpringParametersRunnerFactory.class)
    @RunWith(Parameterized.class)
    @ContextConfiguration(locations = {"/test-context.xml", "/mvc-context.xml"})
    @WebAppConfiguration
    @Transactional
    @TransactionConfiguration
    public class MyTransactionalTest {
    
      @Parameters
      public static Collection<Object[]> params() throws Exception {
        final MyTransactionalTest fakeInstance = new MyTransactionalTest();
        final TestContextManager contextManager = new TestContextManager(MyTransactionalTest.class);
        contextManager.prepareTestInstance(fakeInstance);
        final WebApplicationContext context = fakeInstance.context;
    
        // Do what you need with Spring context, you can even access web resources
        final Resource[] files = context.getResources("path/files");
        final List<Object[]> params = new ArrayList<>();
        for (Resource file : files) {
          params.add(new Object[] {file});
        }
        return params;
      }
    
      @Parameter
      public Resource file;
    
      @Autowired
      private WebApplicationContext context;
    }
    

    【讨论】:

      【解决方案6】:

      我将以下解决方案与 Parameterized.class 一起使用,没有任何问题: http://bmocanu.ro/coding/320/combining-junit-theoriesparameterized-tests-with-spring/

      @ContextConfiguration(value = "classpath:test-context.xml")
      public abstract class AbstractJunitTest extends AbstractJUnit4SpringContextTests {
          private static TestContextManager testContextManager = null;
          private static DAOFactory daoFactory = null;
      
          @Before
          public void initApplicationContext() throws Exception {
              if (testContextManager == null) {
                  testContextManager = new TestContextManager(getClass());
                  testContextManager.prepareTestInstance(this);
                  daoFactory = (DAOFactory)applicationContext.getBean("daoFactory");
              }
          }
      
          protected DAOFactory getDaoFactory() throws Exception {
              return daoFactory;
          }
      }
      
      
      
      @RunWith(Parameterized.class)
      public class SomeTestClass extends AbstractJunitTest {
           ...
      }
      

      【讨论】:

        【解决方案7】:

        记住 Spring 使用 @Autowired 注入,但也使用 setter。所以不要使用@Autowired,而是使用setter:

        private static String directory;
        
        public void setDirectory(String directory) {
            this.directory = directory;
        }
        
        public static String getDirectory() {
             return directory;
        }
        

        【讨论】:

          猜你喜欢
          • 2011-01-28
          • 1970-01-01
          • 2011-02-10
          • 1970-01-01
          • 2015-03-13
          • 1970-01-01
          • 1970-01-01
          • 2017-11-30
          • 2016-02-09
          相关资源
          最近更新 更多