@Autowired inside SpringRunner SpringBootTest Unit Test Cases

1.5k views Asked by At

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
1

There are 1 answers

0
Shamil Puthukkot On

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:

package com.company;

@RunWith(SpringRunner.class)
public class TransferTest {

    @Mock
    IService1 someService1Mock;

    @Mock
    IService2 someService2Mock


    @InjectMocks
    private AccountTestUtils accountTestUtils;

    @Test
    public void basicTransferTestExpectSuccess() {
       accountTestUtils.createAccount();
       ...
    }
}