Middle
What tests do you write and how?
sobes.tech AI
Answer from AI
I write the following types of tests:
- Unit tests: Test individual components or class methods in isolation from the rest of the system. I use JUnit or TestNG. For mocking dependencies — Mockito.
- Integration tests: Test interactions between multiple components, subsystems, or services. For example, interaction with a database or external API. I use Spring Boot Test for testing Spring applications, Testcontainers for testing with real dependencies (databases, message brokers).
- Component tests / End-to-End (E2E) tests: Test the system from the user's perspective from start to finish. Mostly automated UI or API tests. For API, I use Rest-Assured. For UI — Selenium (although I prefer writing API tests as they are more stable and faster).
Test writing process:
- Choosing a framework: JUnit 5 or TestNG for unit tests, Spring Boot Test, Rest-Assured, Testcontainers for integration and component tests.
- Writing tests before or during code development: I use TDD (Test-Driven Development) or write tests immediately after implementing functionality.
- Test isolation: For unit tests, I use Mockito to mock dependencies, testing only the isolated component.
- Test structure: I usually follow the Arrange-Act-Assert structure.
- Clear test naming: Test names should clearly describe what is being tested and under what conditions. For example,
userService_createUser_shouldSaveUserInDatabase. - Testing boundary conditions and errors: I check both normal scenarios and cases with incorrect input data, exceptions, and boundary values.
- Refactoring tests: I keep tests clean and readable, just like the main code.
Example of a unit test with Mockito and JUnit 5:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock // Dependency we mock
private UserRepository userRepository;
@InjectMocks // Service we test
private UserService userService;
@Test
void createUser_shouldSaveUserInDatabase() {
// Arrange: Prepare data and mock behavior
User user = new User("Alice", "alice@example.com");
when(userRepository.save(any(User.class))).thenReturn(user); // Mock save method
// Act: Perform the action
User createdUser = userService.createUser(user);
// Assert: Verify the result
assertEquals("Alice", createdUser.getName()); // Check user creation with correct name
verify(userRepository, times(1)).save(any(User.class)); // Verify save was called once
}
}
Example of using Rest-Assured for an API integration test:
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
class UserControllerIntegrationTest {
@BeforeAll
static void setup() {
// Setup base URL for tests
RestAssured.baseURI = "http://localhost:8080";
RestAssured.port = 8080;
}
@Test
void getUserById_shouldReturnUser() {
// Perform request and verify response
given()
.when()
.get("/users/1") // URL to get user by ID
.then()
.statusCode(200) // Check response status
.body("name", equalTo("John Doe")); // Check response body field
}
}