简体   繁体   中英

Spring & JUnit: Spring instantiates two instances of test class, only injects dependencies for one, runs the other one

What I'm trying to do : Run a single JUnit test (one JUnit test class with a single test method). Test class has one dependency that I expect Spring to handle (inject) prior to running the test.

What I see : Test class is instantiated twice, dependency is satisfied (injected) for one of the instances but not the other. Test is run using the instance for which dependencies are not injected.

My guess : I'm speculating that the test class is instantiated twice because Spring is using the first instance to analyze dependencies (probably using reflection to look for annotations, which I'm not using).

But : I cannot for my life figure out why the test class instance for which the dependency is not created/injected is used to run the test.

test.xml

<beans ...>
  <bean id="test" class="com.example.MyTestClass">
    <property name="primaryDependency" ref="pridep"/>
  </bean>

  <bean id="pridep" class="com.example.MyPrimaryDependency">
    <property name="dataSource" ref="ds"/>
  </bean>

  <bean id="ds" class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"/>
</beans>

MyTestClass.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:test.xml"})
public class MyTestClass {

  private IPrimaryDependency  mDep = null;

  public MyTestClass() {
  }

  public void setPrimaryDependency(IPrimaryDependency dep) {
    mDep = dep;
  }

  @Before
  public void createImageData() {
  }

  @Test
  public void testImageSearch() {
    assertNotNull("mDep is null", mDep);
  }
}

MyPrimaryDependency.java

public class MyPrimaryDependency extends RequiresADataSource implements IPrimaryDependency {

  public MyPrimaryDependency() {
  }
}

Existing related question : Spring dependency injection null when running a test case , but I'm not able to discern what's going on from that answer.

Sorry for the rookie question-- I've been scouring documentation (and bothering colleagues) for several days and am baffled by this.

Thanks!

Add @Autowired

@Autowired
private IPrimaryDependency mDep;

Explanation:

JUnit creates brand new instances of test classes for each test. Spring JUnit runner only extends this behaviour by "preparing" created objects which basically means setting up application context and injecting annotation driven dependencies.

In your case the test class is instantiated twice: From test.xml (the property is injected there) and by JUnit runner to run the first test method. However when created by JUnit, Spring has no idea that it needs to inject something because there are no annotations in the class thus the property ends up being null.

See source code of SpringJUnit4ClassRunner and DependencyInjectionTestExecutionListener for implementation details.

Your test class is

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:test.xml"})
public class MyTestClass {

You run this class with JUnit. Because of @RunWith , JUnit uses an instance of SpringJUnit4ClassRunner to instantiate the class and run its tests.

Because of @ContextConfiguration , the ClassRunner will generate an ApplicationContext by loading your test.xml file.

That test.xml file has a <bean> definition for your test class

<bean id="test" class="com.example.MyTestClass">
    <property name="primaryDependency" ref="pridep"/>
</bean>

So Spring will also generate an instance and set the appropriate property.


Basically, what I'm getting at is that your test class shouldn't be a bean in the Spring context. Create a different test class and inject the bean that has an injection target and inject that into your test class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM