open-source-ideas / ideas

💡 Looking for inspiration for your next open source project? Or perhaps you've got a brilliant idea you can't wait to share with others? Open Source Ideas is a community built specifically for this! 👋
6.59k stars 220 forks source link

A Java tool using Spring that records JUnit + Mockito unit/integration tests from runtime calls #281

Open ibreaz opened 3 years ago

ibreaz commented 3 years ago

Project description

I imagine this tool would work like this: You mark a method with an @RecordTest annotation. You mark some injected dependencies with @RecordMockForTest annotation. You run the project and interact with UI/API. Context, arguments and results are retrieved using Aspect Oriented Programing and Reflection and used to generate a test for each function call in the console or to a disk file.

I know Test Driven Development is a great way to write software, but there are cases when generating a test from existing code might still be needed for various reasons:

Example: Let's say you have some function for calculating Employee salary that does not have automated tests yet:

public class SalaryService {
    public double computeEmployeeSalary(int employeeId) throws Exception {
        // ...
        Employee employee = employeeRepository.getEmployee(employeeId);
        // ...
        return salary;
    }
}

public class EmployeeRepository {
    public Employee getEmployee(int id) throws Exception {
        // ...
        // Get Employee data from DB
        // ...
        return employee;
    }
}

You add @RecordTest to computeEmployeeSalary function to mark that you want tests generated from the calls to this function. You add @RecordMockForTest to EmployeeRepository class to mark that you want this class mocked in the tests. You interact with the UI/API and the computeEmployeeSalary function is called with real data.

The resulted test will be like this (depending on the real data):

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import com.sampleapp.model.Department;
import com.sampleapp.model.Employee;
import com.sampleapp.services.EmployeeRepository;
import static org.mockito.Mockito.*;

class SalaryServiceTest {
    @Test
    void computeEmployeeSalary() throws Exception {
        // Arrange
        Department department1 = Department.builder()
            .id(100)
            .name("IT")
            .build();
        Employee employee1 = Employee.builder()
            .id(1)
            .firstName("Doe")
            .lastName("John")
            .department(department1)
            .salaryParam1(1000.0)
            .salaryParam2(1500.0)
            .salaryParam3(200.0)
            .build();
        EmployeeRepository employeeRepository = mock(EmployeeRepository.class);
        when(employeeRepository.getEmployee(1)).thenReturn(employee1);
        SalaryService salaryService = new SalaryService(employeeRepository);

        // Act
        double result = salaryService.computeEmployeeSalary(1);

        // Assert
        assertEquals(4000.0, result);
    }
}

I already have a prof of concept for this and it would be very helpful for me to hear your opinions before investing more effort in this direction.

Relevant Technology

Java, Spring, JUnit, Mockito

Complexity and required time

Complexity

Required time (ETA)

Categories