Our Spring Boot REST API currently has a pretty large unit test repository. The unit tests refactor common reusable test code out into TestUtil
classes which are @Component
annotated.
It appears that the SpringRunner
unit test cases can only find the @Autowired TestUtil
classes if they are imported as part of the class parameter of the @SpringBootTest
annotation.
Additionally, all @Autowired
variables inside the TestUtil
classes also need to be imported in the classes parameter of the @SpringBootTest
annotation.
Since we have about 30 unit test case classes and each class needs to import roughly 40 other classes in the @SpringBootTest
annotation, one can imagine how unmaintainable this has become.
If a class is not imported as part of the @SpringBootTest
classes parameter, the following error is thrown
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.company.FeeTest': Unsatisfied dependency expressed through field 'accountTestUtils'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.company.accountTestUtils' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Does anybody know of a better way to use @Autowired
annotation inside Unit Test Cases without having to explicitly import them inside the @SpringBootTest
annotation?
A code example is below
FeeTest.java
package com.company;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {
AccountTestUtils.class,
... need to list any @Autowired component in AccountTestUtils
})
public class FeeTest {
@Autowired
private AccountTestUtils accountTestUtils;
@Test
public void basicFeeTestExpectSuccess() {
accountTestUtils.createAccount();
...
}
}
TransferTest.java
package com.company;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {
AccountTestUtils.class,
... need to list any @Autowired component in AccountTestUtils
})
public class TransferTest {
@Autowired
private AccountTestUtils accountTestUtils;
@Test
public void basicTransferTestExpectSuccess() {
accountTestUtils.createAccount();
...
}
}
AccountTestUtils.java
package com.company;
@Component
public class AccountTestUtils {
@Autowired
private IService1 someService1;
@Autowired
private IService2 someService2;
@Autowired
private SomeRepository someRepository1;
public void createAccount() {
someService1.doSomething();
someService2.doSomething();
someRepository2.doSomething();
}
}
Our package structure is the common maven structure
/src
/main
/java
/resources
/test
/java
/resources
Let me talk from the pov of TransferTest.java class.
This class tests the AccountUtilsTest class. So just supply the dependency of AccountUtilTest class to TransferTest.Mock each and every dependency which is auto-wired and used in AcccountUtilsTest.
eg: