S.min Chen
S.min Chen

Reputation: 53

How to run Spring unit tests with a different JUnit Runner?

I'm testing an event processor in multithread. So I use the concurrent-junit of vmlens in my test case. But I got nullpoint exceptions when I autowired beans since I was using ConcurrentTestRunner instead of SpringJunit4ClassRunner. This is my 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>

Test case source code:

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

Obviously, beans were not injected correctly. Is there any way to fix this? Should I extend AbstractJUnit4SpringContextTests?


Attached latest code here:

EventStore is a Jpa repository:

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 is defined just for test.

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

Test case:

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();
    ......
    }
}

Upvotes: 4

Views: 1672

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279930

Since Spring 4.2, when you need to provide your own JUnit Runner for some other reason (eg. ConcurrentTestRunner or Mockito's MockitoJUnitRunner), Spring provides a separate mechanism to initialize the unit test's ApplicationContext and meta configuration.

This mechanism is a combination of two JUnit 4 rules. This is documented in the official documentation, here.

The org.springframework.test.context.junit4.rules package provides the following JUnit 4 rules (supported on JUnit 4.12 or higher).

  • SpringClassRule
  • SpringMethodRule

SpringClassRule is a JUnit TestRule that supports class-level features of the Spring TestContext Framework; whereas, SpringMethodRule is a JUnit MethodRule that supports instance-level and method-level features of the Spring TestContext Framework.

Your unit test class should minimally contain

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

Here's a complete example that utilizes MockitoJUnitRunner while still making use of Spring's TestContext framework:

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

Upvotes: 4

Related Questions