Manuel Jordan
Manuel Jordan

Reputation: 16271

Trying to generate error about Spring's @Autowired field injection for JUnit

I am working with Spring 4.0.7, and with JUnit about testing of course.

About DI Spring offers @Autowired to be used in three locations

I always work through the two first, why never the third option?

Because I remember have read long time ago about field injection should not be used, because it has a negative consequence about Testing. It does JUnit fails.

Note: Only for Testing is the problem. To runtime or production all goes well

Objective: For demonstration/academic purposes I want generate this problem.

I have the following:

Repository

public interface PersonRepository extends JpaRepository<Person, String>{

}

A Service

@Service
@Transactional
@Profile("failure")
public class PersonFailTestServiceImpl implements PersonService {

    private static final Logger logger = ...

    @Autowired
    private PersonRepository personRepository; 

Other Service (calling or using the service shown above)

@Service
@Transactional
@Profile("failure")
public class PersonFailTestProcessImpl implements PersonProcess {

    private static final Logger logger = ...

    @Autowired
    private PersonService personService;

How you can see the two services are based on Field Injection.

Now the testing:

How the beans are loaded

    @Configuration
    @ComponentScan( basePackages={"com.manuel.jordan.server.infrastructure"},
                    basePackageClasses={PersonProcess.class,PersonRepository.class, PersonService.class})
    public class CentralConfigurationEntryPoint {

    }

    @ContextConfiguration(classes=CentralConfigurationEntryPoint.class)
    public class CentralTestConfigurationEntryPoint {
    }

Now the two testing classes

@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles({"development","failure"})
public class PersonServiceImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint {

    @Autowired
    private PersonService personService;

    @Test
    public void savePerson01(){
        Person person01 = PersonFactory.createPerson01();
        personService.save(person01);
        personService.printPerson(personService.findOne("1"));
    }

@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles({"development","failure"})
public class PersonProcessImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint{

    @Autowired
    private PersonProcess personProcess;

Well all the testing methods pass, all green. I don't know if I am missing something or through Spring 4 the problem has been fixed

Upvotes: 0

Views: 390

Answers (2)

user2544039
user2544039

Reputation: 46

I think I can help. The example code you've posted here is a good example of a system / integration test, not a UNIT test.

If you were UNIT testing PersonFailTestProcessImpl, you would have to set the personRepository dependency yourself through code. But it is private, so how do you do this? You cannot use a constructor or setter since none is provided. This is what is meant by 'hard to unit test'.

Java 5+ provides a way to set private variables like this via reflection (the so-called privileged accessor). Basically, you obtain the class, get the declared field, call its setAccessible method, then you can set its value directly. There are libraries that will do these steps for you, but the point is that this is a pain compared to X.setSomething();

So there is nothing that 'makes jUnit fails' by using @Autowired on a private field. But building an object model without constructors or setters for establishing dependencies is unnecessarily constraining.

Upvotes: 1

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279890

If this was your premise or problem

Because I remember have read long time ago about field injection should not be used, because it has a negative consequence about Testing. It does JUnit fails.

then you thought wrong. There is nothing inherently wrong with using field injection, definitely nothing that would cause JUnit tests to fail in and of itself. If a bean exists, Spring will be able to inject it whether it's in a constructor, a setter method, or a field.

Since you've activated your failure profile, your PersonFailTestServiceImpl bean will be found.

Upvotes: 2

Related Questions