4

Am developing MicroServices in springBoot. Am writing unit test for Service and DAO layer. When I use @SpringBootTest it starting application on build. But It should not start application when I run unit test. I used @RunWith(SpringRunner.class), But am unable to @Autowired class instance in junit class. How can I configure junit test class that should not start application and how to @Autowired class instance in junit class.

user3410249
  • 161
  • 4
  • 14

4 Answers4

4

With Spring Boot you can start a sliced version of your application for your tests. This will create a Spring Context that only contains a subset of your beans that are relevant e.g. only for your web layer (controllers, filters, converters, etc.): @WebMvcTest.

There is a similar annotation that can help you test your DAOs as it only populates JPA and database relevant beans (e.g. EntitiyManager, Datasource, etc.): @DataJpaTest.

If you want to autowire a bean that is not part of the Spring Test Context that gets created by the annotatiosn above, you can use a @TestConfiguration to manually add any beans you like to the test context

@WebMvcTest(PublicController.class)
class PublicControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @TestConfiguration
  static class TestConfig {

    @Bean
    public EntityManager entityManager() {
      return mock(EntityManager.class);
    }

    @Bean
    public MeterRegistry meterRegistry() {
      return new SimpleMeterRegistry();
    }

  }
}
rieckpil
  • 10,470
  • 3
  • 32
  • 56
2

Use MockitoJUnitRunner for JUnit5 testing if you don't want to start complete application.

Any Service, Repository and Interface can be mocked by @Mock annotation.

@InjectMocks is used over the object of Class that needs to be tested.

Here's an example to this.

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class AServiceTest {
    
    @InjectMocks
    AService aService;
    
    @Mock
    ARepository aRepository;
    
    @Mock
    UserService userService;
    
    
    @Before
    public void setUp() {
        // MockitoAnnotations.initMocks(this);
        // anything needs to be done before each test.
    }
    
    @Test
    public void loginTest() {
        Mockito.when(aRepository.findByUsername(ArgumentMatchers.anyString())).thenReturn(Optional.empty());
        String result = aService.login("test");
        assertEquals("false", result);
    }
Nakul Goyal
  • 593
  • 4
  • 12
  • The Requirement should be connect to database to fetch records. If mock object we can't get real time data. Is this possible using @ContextConfiguration – user3410249 Nov 11 '20 at 08:01
  • @user3410249 if you want to perform unit testing, you can mock the database records itself. As MockitoJUnitRunner is the fastest way to runt he tests without running application or external db's. Option is you cn use Mockito.when(...).thenReturn() to return mocked records from database, which is better way for unit testing. But if you want realtime connection to db, your application will start. – Nakul Goyal Nov 11 '20 at 09:14
  • Thank you. I got better understand. – user3410249 Nov 11 '20 at 14:12
  • you missed an additional annotation that's required, `@ExtendWith(MockitoExtension.class)`, otherwise the mocks aren't initialized. – gene b. Mar 23 '23 at 22:42
  • 1
    Also, you still have `@SpringBootTest` along with `RunWith(MockitoJunitRunner.class` which is wrong. It interferers with the Mockito Runner and still loads the whole app. You need to remove `SpringBootTest`. – gene b. Mar 24 '23 at 13:48
2

Depending your test setup, if you don't want to autowire a mock but the "real thing", You could simply annotate your test class to include exactly the classes you need (plus their transitive dependencies if necessary)

For example :

@SpringJUnitConfig({ SimpleMeterRegistry.class })

or

@SpringJUnitConfig
@Import({ SimpleMeterRegistry.class })

or

@SpringJUnitConfig
@ContextConfiguration(classes = { SimpleMeterRegistry.class })

See working JUnit5 based samples in here Spring Boot Web Data JDBC allin .

Ari Manninen
  • 306
  • 2
  • 7
2

To avoid running the whole app, include the following Mockito annotations, and exclude @SpringBootTest.

@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)

The final unit test is:

@RunWith(MockitoJUnitRunner.class) // No @SpringBootTest! just Mockito RunWith/ExtendWith
@ExtendWith(MockitoExtension.class)
class Ar11ApplicationTest {

    @Mock // what we're mocking
    Ar11ApplicationService ar11ApplicationService;
    
    @Mock // what we're mocking
    EmailService emailService;
    
    @InjectMocks  // what we're actually testing
    Ar11Application ar11Application;
    
    @BeforeEach
    void setUp() throws Exception {
        
        // Set up an example mock behavior 
         Mockito.when(ar11ApplicationService.processEligibleRecords(
                                    Mockito.anyString(), Mockito.anyString(),                                                                
                                    Mockito.anyString(), Mockito.anyString(), 
                                    Mockito.anyString(), Mockito.anyString(), 
                                    Mockito.anyString())
                 .thenReturn(new Ar11ResultBean(500));
    }

    @Test
    void runTest1() throws Exception {
          //..
    }

(But be aware that if you have some App Test Properties YAML, you need @SpringBootTest to read them in -- e.g. application-test.yaml. In that case you can't avoid @SpringBootTest. It can be used with @Import to import custom mock classes for any objects -- example here.)

gene b.
  • 10,512
  • 21
  • 115
  • 227