如何使用带有使用Spring注入的字段的参数化JUnit测试运行器?

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

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

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

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

@RunWith(Parameterized.class) public class MyTest { @Parameters public static Collection 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 } 

您可以使用Spring的TestContextManager。 在这个例子中,我使用Theories而不是Parameterized。

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

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

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

这意味着对MockitoJUnitRunnerParameterized等任何Runner的第一类支持:

 @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 params() { return Arrays.asList(new Long[][] { {0, 0}, {1, 1} }); } } 

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

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

我对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 { ... } 

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

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

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

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

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

 @RunWith(Parameterized.class) @ContextConfiguration(locations = {"/test-context.xml", "/mvc-context.xml"}) @WebAppConfiguration @ActiveProfiles("test-profile") public class MyTest { @Parameters public static Collection 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 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; } 

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

参数化工厂,完全支持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支持,如测试事务 ,重新启动脏上下文和servlet测试 。 当然,不再需要将伪测试实例中的自动assembly字段传递给参数化测试。

 @UseParametersRunnerFactory(SpringParametersRunnerFactory.class) @RunWith(Parameterized.class) @ContextConfiguration(locations = {"/test-context.xml", "/mvc-context.xml"}) @WebAppConfiguration @Transactional @TransactionConfiguration public class MyTransactionalTest { @Parameters public static Collection 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 params = new ArrayList<>(); for (Resource file : files) { params.add(new Object[] {file}); } return params; } @Parameter public Resource file; @Autowired private WebApplicationContext context; } 

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

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