如何使用不同的JUnit Runner运行Spring单元测试?

时间:2018-02-12 17:45:52

标签: java multithreading junit

我在多线程中测试事件处理器。所以我在我的测试用例中使用了vmlens的concurrent-junit。 但是当我使用ConcurrentTestRunner而不是SpringJunit4ClassRunner时,我在autowired bean时得到了nullpoint异常。 这是我的pom

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.vmlens</groupId>
        <artifactId>concurrent-junit</artifactId>
        <version>1.0.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>2.6.0</version>
        <scope>test</scope>
    </dependency>

测试用例源代码:

import com.anarsoft.vmlens.concurrent.junit.ConcurrentTestRunner;
import com.anarsoft.vmlens.concurrent.junit.ThreadCount;

@RunWith(ConcurrentTestRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class EventListenerTest {
    @Autowired
    private EventStore es; //defined in applicationContext.xml
    @Autowired
    private EntityAppender ea;  //a @Component
......
    @Test
    @ThreadCount(10)
    public final void testDefaultListener() {
    Long bef = es.countStoredEvents();// nullpoint exception
    TestEntity1 t1 = ea.appWithDefaultListener();// nullpoint exception
    ......
    }
}

显然,豆子没有正确注射。 有没有什么办法解决这一问题?我应该扩展AbstractJUnit4SpringContextTests吗?

此处附上最新代码:

EventStore是一个Jpa存储库:

public interface EventStore extends JpaRepository<DomainEvent, Long>{};

的applicationContext.xml

<aop:config proxy-target-class="true" />
<context:annotation-config />
<jpa:repositories base-package="com.my"></jpa:repositories>

EntityAppender仅用于测试。

@Component
public class EntityAppender {
    @Autowired
    private TestEntity1Repository myRepository; //another Jpa repository

    public EntityAppender() {
        super();
    }

    @Transactional
    public TestEntity1 appWithDefaultListener() {
        TestEntity1 t1 = new TestEntity1(UUID.randomUUID().toString().replaceAll("-", ""), "aaaaaaaaaaaa", 44,
                LocalDate.now());
        return myRepository.save(t1);
    }
...
}

测试用例:

import com.anarsoft.vmlens.concurrent.junit.ConcurrentTestRunner;
import com.anarsoft.vmlens.concurrent.junit.ThreadCount;

@RunWith(ConcurrentTestRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class EventListenerTest {

    @ClassRule
    public static final SpringClassRule springClassRule = new SpringClassRule();
    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired
    private EventStore es;
    @Autowired
    private EntityAppender ea;
......
    @Before
    public void setUp() throws Exception {
        bef = es.count();  //<====nullpoint exception due to es being null here
    }

    @Test
    @ThreadCount(10)
    public final void testDefaultListener() {
    bef = es.count();   //<======  es worked well here
    TestEntity1 t1 = ea.appWithDefaultListener();
    ......
    }
}

1 个答案:

答案 0 :(得分:3)

从Spring 4.2开始,当你因某些其他原因需要提供自己的JUnit Runner时(例如ConcurrentTestRunner或Mockito的MockitoJUnitRunner),Spring提供了一个单独的机制初始化单元测试的ApplicationContext和元配置。

此机制是两个JUnit 4规则的组合。这在官方文档here中有记录。

  

org.springframework.test.context.junit4.rules包提供了   遵循JUnit 4规则(在JUnit 4.12或更高版本上受支持)。

     
      
  • SpringClassRule
  •   
  • SpringMethodRule
  •   
     

SpringClassRule是一个支持类级功能的JUnit TestRule   Spring TestContext框架;而SpringMethodRule是一个   JUnit MethodRule,支持实例级和方法级   Spring TestContext Framework的特性。

您的单元测试类应该至少包含

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

这是一个使用MockitoJUnitRunner同时仍然使用Spring的TestContext框架的完整示例:

@ContextConfiguration(classes = ConfigExample.class)
@RunWith(MockitoJUnitRunner.class)
public class Example {
    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired
    private Foo foo;

    @Mock
    private Runnable runnable;

    @Test
    public void method2() {
        Mockito.doNothing().when(runnable).run();
        foo.bar(runnable);
        Mockito.verify(runnable).run();
    }
}

@Configuration
class ConfigExample {
    @Bean
    public Foo Foo() {
        return new Foo();
    }
}

class Foo {
    public void bar(Runnable invoke) {
        invoke.run();
    }
}