Michal Krasny
Michal Krasny

Reputation: 5916

How to combine Mockito and Spring in TestNG

We are building an application which uses Spring Boot. We write unit tests using TestNG and Mockito. However I find it pretty annoying to write when(...) configuration, I would like to use real components instead. I started to use @Spy components instead of mocks and this works pretty well until I need to put a Spy into a Spy. I'd like to avoid loading a Spring Context if possible, because creation of the context is very slow it looks like overkill for me to load it for at max 5 classes.

Is there any way, how could I use real code instead of Mocks and not loading whole Spring context? Or is my approach wrong at all and I should mock out all other classes then the tested one?

Upvotes: 0

Views: 2509

Answers (3)

rahul
rahul

Reputation: 1122

If you are looking to configure your testcase using testng with Spring then you to mention

@ContextConfiguration(locations={
        "/context.xml","/test-context.xml"})

at class level to load you spring file and extends your class org.springframework.test.context.testng.AbstractTestNGSpringContextTests

Sample https://dzone.com/articles/spring-testing-support-testng

Upvotes: 1

ndrone
ndrone

Reputation: 3572

The other way to do this and may take some modifying of code on your end is to do it by constructor injection instead of field injection. Basically taking away any need of the spring context for testing. so the same from the other answer

Class to test

@Service
public class RecordServiceImpl implements RecordService
{

    private final RecordRepository recordRepository;

    @Autowired
    public RecordServiceImpl(RecordRepository recordRepository)
    {
        this.recordRepository = recordRepository;
    }

    public Record find(String id)
    {
        return recordRepository.findOne(id);
    }

    public List<Record> findAll()
    {
        return recordRepository.findAll();
    }

    @Transactional
    public Record save(Record record)
    {
        record.setRecordStatus("F");
        return recordRepository.save(record);
    }
}

Test Case

//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
//    @Autowired
    private RecordRepository recordRepository = Mockito.mock(RecordRepository.class);
//    @Autowired
    private RecordService    recordService;

    @Before
    public void setup()
    {
        Mockito.reset(recordRepository);
        recordService = new RecordServiceImpl(recordRepository);
    }

    @Test
    public void testFind()
    {
        Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);

        Record record = recordService.find("1");
        Assert.assertNull(record);
        Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
    }

    @Test
    public void testSave()
    {
        Mockito.when(recordRepository.save(Mockito.any(Record.class)))
                .thenAnswer(new Answer<Record>()
                {
                    @Override
                    public Record answer(InvocationOnMock invocation) throws Throwable
                    {
                        Record record = (Record) invocation.getArguments()[0];
                        Assert.assertEquals("F", record.getRecordStatus());
                        return record;
                    }
                });

        Record record = new Record();
        record = recordService.save(record);
        Assert.assertNotNull(record);
        Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
    }

    @Test
    public void findAll()
    {

        Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());

        List<Record> records = recordService.findAll();
        Assert.assertNotNull(records);
        Assert.assertEquals(0, records.size());
        Mockito.verify(recordRepository, Mockito.times(1)).findAll();

    }
}

Upvotes: 2

ndrone
ndrone

Reputation: 3572

I think your looking for like this with the use of @ContextConfiguration and @Configuration

Class to test

@Service
public class RecordServiceImpl implements RecordService
{
    @Autowired
    private RecordRepository recordRepository;

    public Record find(String id)
    {
        return recordRepository.findOne(id);
    }

    public List<Record> findAll()
    {
        return recordRepository.findAll();
    }

    @Transactional
    public Record save(Record record)
    {
        record.setRecordStatus("F");
        return recordRepository.save(record);
    }
}

Test Class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
    @Autowired
    private RecordRepository recordRepository;
    @Autowired
    private RecordService    recordService;

    @Before
    public void setup()
    {
        Mockito.reset(recordRepository);
    }

    @Test
    public void testFind()
    {
        Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);

        Record record = recordService.find("1");
        Assert.assertNull(record);
        Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
    }

    @Test
    public void testSave()
    {
        Mockito.when(recordRepository.save(Mockito.any(Record.class)))
                .thenAnswer(new Answer<Record>()
                {
                    @Override
                    public Record answer(InvocationOnMock invocation) throws Throwable
                    {
                        Record record = (Record) invocation.getArguments()[0];
                        Assert.assertEquals("F", record.getRecordStatus());
                        return record;
                    }
                });

        Record record = new Record();
        record = recordService.save(record);
        Assert.assertNotNull(record);
        Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
    }

    @Test
    public void findAll()
    {

        Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());

        List<Record> records = recordService.findAll();
        Assert.assertNotNull(records);
        Assert.assertEquals(0, records.size());
        Mockito.verify(recordRepository, Mockito.times(1)).findAll();

    }
}

Test Class Configuration

@Configuration
public class RecordServiceTestConfig
{
    @Bean
    public RecordService recordService()
    {
        return new RecordServiceImpl();
    }

    @Bean
    public RecordRepository recordRepository()
    {
        return Mockito.mock(RecordRepository.class);
    }
}

the entire test class took 714ms to run the findAll test took 1ms.

Upvotes: 1

Related Questions