java 如何将参数化 JUnit 测试运行程序与使用 Spring 注入的字段一起使用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3405422/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 01:41:22  来源:igfitidea点击:

How can I use the Parameterized JUnit test runner with a field that's injected using Spring?

javaspringjunit

提问by Edward Dale

I'm using Spring to inject the path to a directory into my unit tests. Inside this directory are a number of files that should be used to generate test data for parameterized test cases using the Parameterizedtest runner. Unfortunately, the test runner requires that the method that provides the parameters be static. This doesn't work for my situation because the directory can only be injected into a non-static field. Any ideas how I can get around this?

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

采纳答案by Jeanne Boyarsky

I assume you are using JUnit 4.X since you mentioned the Parameterized test runner. This implies you aren't using @RunWith(SpringJUnit4ClassRunner). Not a problem, just listing my assumptions.

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

The following uses Spring to get the test files directory from the XML file. It doesn't inject it, but the data is still available to your test. And in a static method no less.

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

The only disadvantage I see is that it may mean your Spring config is getting parsed/configured multiple times. You could load just a smaller file with test specific info if need be.

我看到的唯一缺点是它可能意味着您的 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
}

回答by Simon LG

You can use a TestContextManager from Spring. In this example, I'm using Theories instead of Parameterized.

您可以使用 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);
  }
}

I found this solution here : How to do Parameterized/Theories tests with Spring

我在这里找到了这个解决方案:如何使用 Spring 进行参数化/理论测试

回答by Abhijit Sarkar

For someone reading this late 2015 or later, Spring 4.2has, in addition to SpringJUnit4ClassRunner added SpringClassRule and SpringMethodRule which leverage the support for Spring TestContext Framework.

对于在 2015 年末或之后阅读这篇文章的人来说,除了 SpringJUnit4ClassRunner 之外,Spring 4.2还添加了 SpringClassRule 和 SpringMethodRule,它们利用了对Spring TestContext Framework的支持。

This means first class support for any Runner like MockitoJUnitRunneror Parameterized:

这意味着对任何 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} });
    }
}

回答by Konstantin Pavlov

It's enough to annotate test class with @RunWith(Parameterized.class)and @ContextConfiguration, use @Autowiredfor dependency injection and use TestContextManagerin constructor for initialization, e.g.:

@RunWith(Parameterized.class)and注释测试类就足够了@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() {
       …
    }
}

回答by Arnor

Here is a first solution without JUnit 4.12 parameterized factory, below an improved solution with it.

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

Static context without transactional support

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

Let Spring do all configuration parsing and autowiring with TestContextManagerclass.

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

The trick is to use a fake test instance to get autowired fields and pass them to the parameterized test which will effectively run.

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

But keep in mind prepareTestInstance()do the autowiring but doesn't manage test transaction and other nice stuffs handled by beforeTestMethod()and afterTestMethod().

但请记住,prepareTestInstance()执行自动装配但不管理测试事务和其他由beforeTestMethod()and处理的好东西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;
}

However a drawback appear if you have a lot of autowired fields because you have to manually pass them to the array parameters.

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

Parameterized factory with full Spring support

具有完整 Spring 支持的参数化工厂

JUnit 4.12 introduce ParametersRunnerFactorywhich allow to combine parameterized test and Spring injection.

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

The factory can be added to previous test class to give full Spring support like test transaction, reinit dirty contextand servlet test. And of course there no more need to pass autowired fields from fake test instance to parameterized test.

该工厂可以添加到以前的测试类中,以提供完整的 Spring 支持,如测试事务、重新初始化脏上下文servlet 测试。当然,不再需要将自动装配的字段从假测试实例传递到参数化测试。

@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;
}

回答by Max

I use the following solution with the Parameterized.class without any problem: http://bmocanu.ro/coding/320/combining-junit-theoriesparameterized-tests-with-spring/

我将以下解决方案与 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 {
     ...
}

回答by Thierry-Dimitri Roy

Remember that Spring inject using @Autowired, but also with setter. So instead of using @Autowired, use the setter:

请记住 Spring 使用 @Autowired 注入,但也使用 setter。因此,不要使用@Autowired,而是使用 setter:

private static String directory;

public void setDirectory(String directory) {
    this.directory = directory;
}

public static String getDirectory() {
     return directory;
}