Reputation: 53
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
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 JUnitTestRule
that supports class-level features of the Spring TestContext Framework; whereas,SpringMethodRule
is a JUnitMethodRule
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