12.2 Order/List CRUD and Business Logic Unit Testing
Based on the knowledge gained, you will learn how to integrate a simple ordering system and verify that your code works correctly.
1. Requirement Definition
- Users can order products (including basic inventory check logic).
- Users can view a list of orders.
- Canceling an order should restore inventory.
2. Domain Model Design (JPA)
@Entity
@Table(name = "orders")
public class Order {
@Id @GeneratedValue
private Long id;
private String itemName;
private int orderPrice;
private int count;
@Enumerated(EnumType.STRING)
private OrderStatus status; // ORDER, CANCEL
// Includes creation methods and business logic (restoring stock on cancellation) - Domain-Driven Design
}
3. Unit Testing
In a Spring Boot environment, you can use JUnit 5 and Mockito to quickly test logic in specific layers.
Service Layer Testing Example
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock OrderRepository orderRepository;
@InjectMocks OrderService orderService;
@Test
void order_success() {
// given
OrderDto dto = new OrderDto("Laptop", 1000, 2);
// when
Long orderId = orderService.createOrder(dto);
// then
verify(orderRepository).save(any(Order.class));
}
}
4. Integration Testing
Use the @SpringBootTest annotation to load actual beans into the container and conduct tests that include database connections.
@SpringBootTest
@Transactional
class OrderFlowIntegrationTest {
@Autowired OrderService orderService;
@Test
void full_order_flow_test() {
// ... Scenario testing using a real database
}
}
5. Transaction Management and Data Integrity
One of the most critical aspects of a practical mini project is Transaction Management. You must declare @Transactional on service methods that manipulate multiple entities to ensure data integrity.
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
@Transactional // Encapsulates the entire method into a single transaction
public Long createOrder(OrderDto dto) {
// 1. Deduct product stock (Throws exception on failure)
Product product = productRepository.findByName(dto.getItemName());
product.removeStock(dto.getCount());
// 2. Create and save order
Order order = Order.createOrder(product, dto.getCount());
orderRepository.save(order);
return order.getId();
}
}
- Guarantees Atomicity: Prevents situations where stock is deducted but the order record fail to save.
- Dirty Checking: When using JPA, changes to entities are automatically reflected in the DB upon transaction completion without explicit
save()calls.
🎯 Key Points
- In real-world projects, it is better to place business logic inside domain entities.
- Unit tests remove external dependencies and quickly verify the logic itself.
- Integration tests provide final confirmation that the overall flow of the system is correct.