如何为List<Overview>编写JUnit单元测试?请求技术协助
Hey there, I totally get how frustrating it is to get stuck writing unit tests for database-related code—especially when you're still wrapping your head around how unit testing works with MongoDB. Let's break this down step by step to get you unblocked!
First, let's clarify: unit testing for this method means isolating your code from the actual MongoDB database. We don't want to hit a real database in unit tests—instead, we'll mock the Mongo-related dependencies (like MongoTemplate or Spring Data MongoRepository) to control their behavior.
Step 1: Define Your Test Scenarios
We need to cover the main paths your method can take:
- Happy path: When a valid
statusis provided, the method returns the correct list ofOverviewobjects. - Error path: When the underlying Mongo operation fails, the method correctly throws your
CustomMongoException.
Step 2: Set Up Test Dependencies
Assuming you're using JUnit 5 and Mockito (the most common combo for Java unit tests), add these dependencies to your build file (Maven example):
<!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <!-- Mockito for mocking dependencies --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency>
Step 3: Write the Test Code
Let's assume your service/class looks like this (adjust if your implementation differs):
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service; import java.util.List; @Service public class OverviewService { private final MongoTemplate mongoTemplate; public OverviewService(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List<Overview> findOverviewByStatus(String status) throws CustomMongoException { try { Query query = Query.query(Criteria.where("status").is(status)); return mongoTemplate.find(query, Overview.class); } catch (MongoException e) { throw new CustomMongoException("Failed to fetch overviews by status", e); } } } // Your custom exception class class CustomMongoException extends Exception { public CustomMongoException(String message, Throwable cause) { super(message, cause); } } // Your Overview model class Overview { private String id; private String status; private String name; // Constructor, getters, setters public Overview(String id, String status, String name) { this.id = id; this.status = status; this.name = name; } public String getId() { return id; } public String getStatus() { return status; } public String getName() { return name; } }
Now here's the unit test class:
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 org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class OverviewServiceTest { // Mock the MongoTemplate so we don't hit a real database @Mock private MongoTemplate mongoTemplate; // Inject the mocked MongoTemplate into our service under test @InjectMocks private OverviewService overviewService; @Test void findOverviewByStatus_ValidStatus_ReturnsMatchingOverviews() throws CustomMongoException { // 1. Prepare test data String testStatus = "ACTIVE"; Overview testOverview1 = new Overview("id1", testStatus, "Q3 Sales Overview"); Overview testOverview2 = new Overview("id2", testStatus, "Q4 Marketing Overview"); List<Overview> expectedOverviews = List.of(testOverview1, testOverview2); // 2. Tell Mockito what to do when mongoTemplate.find() is called when(mongoTemplate.find(any(Query.class), eq(Overview.class))) .thenReturn(expectedOverviews); // 3. Run the method we're testing List<Overview> actualOverviews = overviewService.findOverviewByStatus(testStatus); // 4. Verify the results are correct assertEquals(expectedOverviews.size(), actualOverviews.size()); assertEquals(expectedOverviews.get(0).getId(), actualOverviews.get(0).getId()); // Make sure mongoTemplate.find() was called exactly once verify(mongoTemplate, times(1)).find(any(Query.class), eq(Overview.class)); } @Test void findOverviewByStatus_MongoFails_ThrowsCustomMongoException() { // 1. Prepare test data String testStatus = "INVALID"; MongoException mockMongoError = new MongoException("Connection timed out"); // 2. Tell Mockito to throw an exception when mongoTemplate.find() is called when(mongoTemplate.find(any(Query.class), eq(Overview.class))) .thenThrow(mockMongoError); // 3. Verify our method throws the custom exception CustomMongoException thrownException = assertThrows(CustomMongoException.class, () -> overviewService.findOverviewByStatus(testStatus)); // Check exception details are correct assertEquals("Failed to fetch overviews by status", thrownException.getMessage()); assertEquals(mockMongoError, thrownException.getCause()); // Ensure mongoTemplate.find() was called once verify(mongoTemplate, times(1)).find(any(Query.class), eq(Overview.class)); } }
Key Things to Keep in Mind
- Mock external dependencies: The goal is to test your code, not MongoDB. Mocking
MongoTemplateorMongoRepositorylets you control exactly what data is returned (or what error is thrown) without needing a real database. - Cover edge cases: Add tests for scenarios like an empty
statusstring (if your method allows it), or when Mongo returns an empty list. - If you use Spring Data Repositories: If your code uses a
MongoRepositoryinterface instead ofMongoTemplate, the approach is almost identical—just mock the repository interface instead ofMongoTemplate.
内容的提问来源于stack exchange,提问作者nico43624

