开发者

Spring Transactional Parameterized Test and Autowiring

Is there a way to get a class that extends AbstractTransa开发者_Go百科ctionalJUnit4SpringContexts to play nicely with JUnit's own @RunWith(Parameterized.class), so that fields marked as Autowired get wired in properly?

@RunWith(Parameterized.class)
public class Foo extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired private Bar bar

    @Parameters public static Collection<Object[]> data() {
        // return parameters, following pattern in
        // http://junit.org/apidocs/org/junit/runners/Parameterized.html
    }

    @Test public void someTest(){
        bar.baz() //NullPointerException
    }
}


See http://jira.springframework.org/browse/SPR-5292 There is a solution.


You can use a TestContextManager from Spring. In this example, I'm using Theories instead of 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);
  }
}

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


You can use SpringClassRule and SpringMethodRule for this purpose

@RunWith(Parameterized.class)
@ContextConfiguration(...)
public class FooTest {

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired 
    private Bar bar

    @Parameters 
    public static Collection<Object[]> data() {
        // return parameters, following pattern in
        // http://junit.org/apidocs/org/junit/runners/Parameterized.html
    }

    @Test 
    public void someTest() {
        bar.baz() //NullPointerException
    }
}


No, you can't. The superclass has:

@RunWith(SpringJUnit4ClassRunner.class)

which assures that the tests are run within spring context. If you replace it, you are losing this.

What comes to my mind as an alternative is to extend SpringJunit4ClassRunner, provide your custom functionality there and use it with @RunWith(..). Thus you will have the spring context + your additional functionality. It will call super.createTest(..) and then perform additional stuff on the test.


I've had to handle the transactions programmatically (see http://www.javathinking.com/2011/09/junit-parameterized-test-with-spring-autowiring-and-transactions/):

@RunWith(Parameterized.class)
@ContextConfiguration(locations = "classpath*:/testContext.xml")
public class MyTest {

    @Autowired
    PlatformTransactionManager transactionManager;

    private TestContextManager testContextManager;

    public MyTest (... parameters for test) {
        // store parameters in instance variables
    }

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

    @Parameterized.Parameters
    public static Collection<Object[]> generateData() throws Exception {
        ArrayList list = new ArrayList();
        // add data for each test here
        return list;
    }

    @Test
    public void validDataShouldLoadFully() throws Exception {
        new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                status.setRollbackOnly();
                try {
                    ... do cool stuff here

                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                return null;
            }
        });

    }


Here's how I made it work in Spring Boot 1.5.7:

  1. Add the @RunWith(Parameterized.class) annotation to your class

  2. Inject your dependency as class field with:

    @Autowired
    private Bar bar;
    
  3. Add your parameter/s as class fields:

    private final int qux;
    private final Boolean corge;
    private final String grault;
    
  4. Add a constructor to initialize the parameter/s as follows:

     public Foo(int qux, Boolean corge, String grault) throws Exception {
         this.qux = qux;
         this.corge = corge;
         this.grault = grault;
         new TestContextManager(getClass()).prepareTestInstance(this);
     }
    
  5. Add a static method data that returns a Collection containing the values of your parameters at each iteration, respecting the order by which they are passed to the constructor:

     @Parameterized.Parameters
     public static Collection<Object[]> data() {
         return Arrays.asList(new Object[][]{
                 { 1, Boolean.FALSE, "Hello" },
                 { 2, Boolean.TRUE, null },
                 { 3, null, "world" }
         });
     }
    
  6. Write your test using the class fields declared above like follows:

    @Test public void someTest(){
        // Some arrangements
    
        // Some actions
    
        assertThat(myTestedIntValue, is(equalTo(qux));
        assertThat(myTestedBooleanValue, is(equalTo(corge));
        assertThat(myTestedStringValue, is(equalTo(grault));
    }
    


Inspired by Simon's solution, you can use TestContextManager also with Parameterized runner:

@RunWith(Parameterized.class)
@ContextConfiguration(locations = "classpath:/spring-context.xml")
public class MyTestClass {

  @Parameters public static Collection data() {
    // return parameters, following pattern in
    // http://junit.org/apidocs/org/junit/runners/Parameterized.html
  }

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

}

Here is full example

I am not sure about handling @Transactional in this case.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜