Reputation: 2197
I have an application that uses SpringBoot for dependency injection and the app works fine, but testing fails because @Autowired fields aren't being injected during tests.
@SpringBootApplication
public class ProcessorInterface {
protected final static Logger logger = Logger.getLogger( ProcessorInterface.class );
public static void main(String[] args) {
try {
SpringApplication.run(ProcessorInterfaceRunner.class, args);
} catch (Exception ex) {
logger.error("Error running ProcessorInterface", ex);
}
}
}
@Component
@Configuration
@ComponentScan
public class ProcessorInterfaceRunner implements CommandLineRunner {
protected final static Logger logger = Logger.getLogger( ProcessorInterface.class );
@Autowired
private RequestService requestService = null;
@Autowired
private ValidatorService validatorService = null;
@Override
public void run(String... args) throws Exception {
ESPOutTransaction outTransaction = null;
outTransaction = new ESPOutTransaction();
// initialize outTransaction fields
...
// done initializing outTransaction fields
if (validatorService.isValid(outTransaction)) {
System.out.println(requestService.getRequest(outTransaction));
} else {
System.out.println("Bad Data");
}
}
}
@Service
public class ESPRequestService implements RequestService<ESPOutTransaction> {
@Autowired
ValidatorService validatorService = null;
@Override
public String getRequest(ESPOutTransaction outTransaction) throws IllegalArgumentException {
if (!validatorService.isValid(outTransaction)) {
throw new IllegalArgumentException("Invalid parameters in transaction object. " + outTransaction.toString());
}
StringBuffer buff = new StringBuffer("create request XML");
buff.append("more XML");
return buff.toString();
}
}
@Service
public class ESPValidatorService implements ValidatorService {
private static org.apache.log4j.Logger logger = Logger.getLogger(ESPValidatorService.class);
// declare some constants for rules
private static final int MAX_LENGTH_XYZ = 3;
@Override
public boolean isValid(OutTransaction outTransaction) {
ESPOutTransaction espOutTransaction = (ESPOutTransaction)outTransaction;
boolean isValid = true;
if (espOutTransaction == null) {
logger.warn("espOutTransaction is NULL");
isValid = false;
} else {
// XYZ is required
if (espOutTransaction.getXYZ() == null) {
logger.warn("XYZis NULL\r\n" + espOutTransaction.toString());
isValid = false;
}
// XYZ max length = MAX_LENGTH_XYZ
if (espOutTransaction.getXYZ() != null && espOutTransaction.getPubCode().trim().length() > MAX_LENGTH_XYZ) {
logger.warn("XYZis too long (max length " + MAX_LENGTH_XYZ + ")\r\n" + espOutTransaction.toString());
isValid = false;
}
}
return isValid;
}
}
These all work and I get good output when I run the app. When I try to test it though, it fails because it can't find ESPValidatorService to inject into ESPRequestService
@RunWith(Suite.class)
@SuiteClasses({ ESPOutTransactionValidatorTest.class, ESPRequestTest.class })
public class AllTests {}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ESPRequestService.class})
public class ESPRequestTest {
@Test
public void testGetRequest() {
ESPRequestService requestService = new ESPRequestService();
String XYZ = "XYZ";
ESPOutTransaction outTransaction = null;
outTransaction = new ESPOutTransaction();
outTransaction.setXYZ(XYZ);
String strRequest = "some expected request XML";
String request = requestService.getRequest(outTransaction);
assertEquals(request, strRequest);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ESPValidatorService.class)
public class ESPOutTransactionValidatorTest {
@Test
public void testIsValid() {
ESPValidatorService validatorService = new ESPValidatorService();
ESPOutTransaction outTransaction = null;
// test request = null
assertFalse(validatorService.isValid(outTransaction));
String XYZ = "XYZ";
outTransaction = new ESPOutTransaction();
outTransaction.setXYZ(XYZ);
// test all good
assertTrue(validatorService.isValid(outTransaction));
// test XYZ
outTransaction.setXYZ(null);
assertFalse(validatorService.isValid(outTransaction));
outTransaction.setXYZ("ABCD"); // too long
assertFalse(validatorService.isValid(outTransaction));
outTransaction.setXYZ(XYZ);
}
}
How can I get the unit tests to auto wire?
Upvotes: 3
Views: 15414
Reputation: 131546
I see two problems :
1) you don't rely on Spring beans but you create instances with the new
operator.
Instead of writing :
ESPRequestService requestService = new ESPRequestService();
you should let Spring inject the instance :
@Bean
ESPRequestService requestService;
2) The @SpringBootTest
configuration is not correct.
In each test, you specified a very specific bean class in the classes
attribute of @SpringBootTest
:
@SpringBootTest(classes = ESPValidatorService.class)
public class ESPOutTransactionValidatorTest {
and
@SpringBootTest(classes = {ESPRequestService.class})
public class ESPRequestTest {
But classes
attributes of @SpringBootTest
serves to specify the annotated classes to use for loading an ApplicationContext.
The annotated classes to use for loading an ApplicationContext. Can also be specified using
@ContextConfiguration(classes=...)
. If no explicit classes are defined the test will look for nested@Configuration
classes, before falling back to aSpringBootConfiguration
search.
So all configuration classes and beans of your application may not be discovered and loaded in the Spring container .
To be able to load all application beans during your tests, the most simple way is not specifying the classes
attribute in the @SpringBootTest
annotation :
@SpringBootTest
public class ESPRequestTest { ...}
It will look for a Spring bean that holds the @SpringBootConfiguration
.
Ideally, it will found the @SpringBootApplication
bean of your application.
If the package of the test class is located inside the package (or at a lower level) of the @SpringBootApplication
class, it should be automatically discovered.
Otherwise the other way is specifying a configuration that will allow to load all required beans :
@SpringBootTest(classes = MySpringBootApplication.class)
public class ESPRequestTest { ...}
Upvotes: 4