Test Naming Conventions
Purpose: Establish clear, consistent naming patterns for test classes and methods to improve readability and maintainability.
Table of Contents
- Test Class Naming
- Test Method Naming
- Test Data Naming
- Given-When-Then Pattern
- Examples from StockEase
- Anti-Patterns to Avoid
- Related Documentation
Test Class Naming
Pattern
{ClassName}Test.java
Rules
- Suffix: Always end with
Test(singular) - Location: Same package structure as production code
- Naming Style: CamelCase, matching the class being tested
- One class per test file: Donβt combine multiple test classes
Examples from StockEase
| Test Class | Production Class | Purpose |
|---|---|---|
AuthControllerTest.java |
AuthController |
Unit tests for auth logic |
ProductControllerTest.java |
ProductController |
Slice tests for general product endpoints |
ProductCreateControllerTest.java |
ProductController |
Slice tests for POST /api/products |
ProductFetchControllerTest.java |
ProductController |
Slice tests for GET endpoints |
ProductUpdateControllerTest.java |
ProductController |
Slice tests for PUT endpoints |
ProductDeleteControllerTest.java |
ProductController |
Slice tests for DELETE endpoints |
ProductPaginationControllerTest.java |
ProductController |
Slice tests for pagination |
StockEaseApplicationTests.java |
(N/A) | Application bootstrap test |
Rationale
- Clear ownership: A test class tests a specific production class
- Easy discovery: Maven Surefire
finds
*Test.javaby default - Familiar convention: Follows Java/JUnit standards
Test Method Naming
General Pattern
{action}_{expectedBehavior}_{givenCondition}
OR (shorter form)
{action}_{scenario}
Detailed Rules
| Component | Guidelines | Example |
|---|---|---|
| Action | Verb starting with test |
testLogin, testCreate,
testFetch |
| Expected Behavior | What should happen | Success, Denied,
ReturnsEmpty,
ValidatesInput |
| Given Condition | When/under what conditions | WithValidCredentials,
WithAdminRole,
WithInvalidData |
Pattern Variants
Variant A: Full Descriptive (Recommended)
void testLoginSuccess_WhenValidCredentialsProvided() { }
void testProductCreation_Denied_WhenUserRoleAttempts() { }
void testGetLowStockProducts_ReturnsEmpty_WhenNoLowStock() { }Pros: Crystal clear intention
Cons: Longer names
Use when: Testing complex scenarios
with specific conditions
Variant B: Concise (Also Acceptable)
void testLoginSuccess() { }
void testProductCreationDenied() { }
void testLowStockProductsEmpty() { }Pros: Shorter, faster to type
Cons: Condition not in name
Use when: Condition is obvious or
commented
Variant C: BDD-Style (Emerging)
void should_LoginSuccessfully_WhenValidCredentialsProvided() { }
void should_DenyProductCreation_WhenUserRoleAttempts() { }
void should_ReturnEmpty_WhenNoLowStockProducts() { }Pros: Explicitly states requirement
as behavior
Cons: Longer, different style
Use when: Working with BDD frameworks
(not currently used)
Test Data Naming
Naming Convention
{semantic}_{characteristic}
Examples
@Test
void testLoginSuccess() {
// β
Good: Descriptive semantic + characteristic
String username = "testuser";
String password = "testpassword";
String role = "ROLE_USER";
String token = "mockToken";
// β
Good: Object names indicate what they represent
User mockUser = new User(1L, username, password, role);
LoginRequest loginRequest = new LoginRequest(username, password);
// β Bad: Generic names
String u = "testuser";
String p = "testpassword";
Object obj = new Object();
}Test Data Characteristics
| Characteristic | Example | When to Use |
|---|---|---|
| mock | mockUser, mockToken |
Data created from mocks |
| valid | validProduct,
validPassword |
Data that should work |
| invalid | invalidQuantity = -1 |
Data that should fail |
| empty | emptyProductList |
Boundary condition |
| expected | expectedStatus,
expectedResponse |
Assertion target |
| actual | actualResponse |
What we got back |
Given-When-Then Pattern
Philosophy
Structure test methods using the Given-When-Then (GWT) pattern as JavaDoc or comments:
Given: [preconditions/setup]
When: [action taken]
Then: [expected outcome]
Example 1: AuthControllerTest
/**
* Given: A user with valid credentials in the database
* When: Login request is made with correct username and password
* Then: Login succeeds and a JWT token is returned
*/
@Test
void testLoginSuccess() {
// Given
String username = "testuser";
String password = "testpassword";
String role = "ROLE_USER";
String token = "mockToken";
User mockUser = new User(1L, username, password, role);
when(userRepository.findByUsername(username)).thenReturn(Optional.of(mockUser));
when(authenticationManager.authenticate(any())).thenReturn(null);
when(jwtUtil.generateToken(username, role)).thenReturn(token);
// When
LoginRequest loginRequest = new LoginRequest(username, password);
ResponseEntity<ApiResponse<String>> responseEntity = authController.login(loginRequest);
// Then
assertThat(responseEntity).isNotNull();
ApiResponse<String> response = responseEntity.getBody();
assertThat(response.isSuccess()).isTrue();
assertThat(response.getData()).isEqualTo(token);
}Example 2: ProductCreateControllerTest
/**
* Given: An admin user is authenticated
* When: A POST request is made to create a product with valid data
* Then: Product is created and returned with 200 OK status
*/
@Test
void testValidProductCreation() throws Exception {
// Given
Product product1 = new Product("Product 1", 10, 100.0);
product1.setId(1L);
product1.setTotalValue(1000.0);
when(productRepository.save(any(Product.class))).thenReturn(product1);
// When
mockMvc.perform(post("/api/products")
.contentType(APPLICATION_JSON)
.content("{\"name\": \"Product 1\", \"quantity\": 10, \"price\": 100.0}")
.with(csrf())
.with(user("adminUser").roles("ADMIN")))
// Then
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Product 1"))
.andExpect(jsonPath("$.quantity").value(10));
}Example 3: Parameterized Test
/**
* Given: A product endpoint and various user roles
* When: Different roles attempt to fetch low-stock products
* Then: Both admin and user roles get success response
*/
@ParameterizedTest
@CsvSource({
"adminUser, ADMIN",
"regularUser, USER"
})
void testLowStockProductsWithRoles(String username, String role) throws Exception {
// Given
Product product1 = new Product("Low Stock Product 1", 3, 50.0);
product1.setId(1L);
when(productRepository.findByQuantityLessThan(5))
.thenReturn(Arrays.asList(product1));
// When
mockMvc.perform(get("/api/products/low-stock")
.with(user(username).roles(role)))
// Then
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("Low Stock Product 1"));
}GWT in JavaDoc Format
/**
* Tests successful login for a regular user.
*
* Given: A user with valid credentials exists in the repository
* When: The login endpoint receives a valid login request
* Then: The response contains a JWT token and success status
*
* @see AuthController#login(LoginRequest)
* @see JwtUtil#generateToken(String, String)
*/
@Test
void testLoginSuccess() {
// ...
}Examples from StockEase
AuthControllerTest Methods
// β
Descriptive, follows Given-When-Then
void testLoginSuccess() { }
void testLoginFailureWithInvalidCredentials() { }
void testLoginFailureWithUserNotFound() { }
void testAdminLoginSuccess() { }ProductControllerTest Methods
// β
Parameterized with role variants
void testLowStockProductsWithRoles(String username, String role) { }
void testLowStockProductsEmptyWithRoles(String username, String role) { }
// β
Clear scenario naming
void testGetProductByIdSuccess() { }
void testGetProductByIdNotFound() { }ProductCreateControllerTest Methods
// β
Clear CRUD operation + outcome
void testValidProductCreation() { }
void testProductCreationDeniedForUser() { }
void testProductCreationWithoutAuth() { }
// β
Parameterized authorization checks
void testProductCreationDeniedForUser(String username, String role) { }ProductUpdateControllerTest Methods
// β
Specific update scenarios
void testValidProductUpdate() { }
void testProductUpdateQuantity() { }
void testProductUpdateDeniedForUser() { }
void testInvalidUpdateNegativeQuantity() { }Anti-Patterns to Avoid
β Anti-Pattern 1: Generic Names
// BAD
void testMethod1() { }
void test() { }
void testMethod() { }
// GOOD
void testLoginSuccess() { }
void testProductCreationDenied() { }β Anti-Pattern 2: Test Names That Donβt Describe Behavior
// BAD
void testUserLogin() { } // Unclear: success or failure?
void testProduct() { } // What about product?
void checkController() { } // Is it a test?
// GOOD
void testLoginSuccessWithValidCredentials() { }
void testProductCreationByAdmin() { }
void testControllerAuthorization() { }β Anti-Pattern 3: Multiple Assertions Without Grouping
// BAD - No Given-When-Then structure
@Test
void testLoginAndProduct() {
login();
assertThat(token).isNotNull();
createProduct();
assertThat(product).isNotNull();
// Why are we mixing login and product in one test?
}
// GOOD - Focused test
@Test
void testLoginSuccess() {
// Given, When, Then for login only
}β Anti-Pattern 4: Unclear Test Data
// BAD
User u = new User(1L, "x", "y", "ROLE_X");
String t = "abc123";
int q = 5;
// GOOD
User testUser = new User(1L, "testuser", "password", "ROLE_USER");
String jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
int lowStockThreshold = 5;β Anti-Pattern 5: Magic Numbers Without Context
// BAD
when(productRepository.findByQuantityLessThan(5)).thenReturn(list);
// GOOD
int LOW_STOCK_THRESHOLD = 5;
when(productRepository.findByQuantityLessThan(LOW_STOCK_THRESHOLD))
.thenReturn(list);Current Test Method Inventory
By Naming Pattern
Pattern:
test{Action}{Outcome}
testLoginSuccessβ Login succeedstestGetProductsEmptyβ GET returns empty listtestValidProductCreationβ Valid product is created
Pattern:
test{Action}Denied (Authorization)
testProductCreationDeniedβ Non-admin blockedtestProductUpdateDeniedForUserβ User role blockedtestProductDeleteDeniedForUserβ User role blocked
Pattern:
test{Action}Invalid (Validation)
testInvalidUpdateQuantityβ Negative quantity rejectedtestInvalidUpdateNegativePriceβ Negative price rejected
Pattern:
test{Action}With{Variant}
(Parameterized)
testLowStockProductsWithRolesβ Multiple roles testedtestProductCreationDeniedForUserβ User/admin variants
Checklist for New Tests
When writing a new test, ensure:
Related Documentation
Testing Fundamentals
- Testing Strategy β Goals, philosophy, scope
- Test Pyramid β Unit/slice/integration breakdown
- Spring Slices β Test class patterns
Implementation Examples
- Controller Integration Tests β MockMvc patterns
- Security Tests β Authorization test patterns
- Test Data & Fixtures β Mock data setup
Main Documentation
- Testing Architecture β Entry point
- Backend Architecture β Code being tested
Last Updated: October 31, 2025
Version: 1.0
Status: β
Based on actual StockEase
test code