测试驱动开发中的副作用测试与条件视图展示
立即解锁
发布时间: 2025-09-05 00:32:30 阅读量: 2 订阅数: 2 AIGC 

### 测试驱动开发中的副作用测试与条件视图展示
#### 1. 视图更新与订单状态反映
当订单状态发生变化时,`OrderButton` 和 `OrderDetail` 都应反映这些变化。`OrderButton` 的文本在顾客未选择任何商品时应为 “Your Order”,而当订单中有商品时则应为 “Your Order $ <order total>”。`OrderDetail` 中的菜品列表和总价摘要也应反映订单状态的变化。
为实现这一点,两个 `ViewModel` 都应期望一个 `OrderController` 引用,并使用它来读取相关信息。可以使用 `@EnvironmentObject` 注入方法来实现:
```swift
// 假设的实现思路
extension OrderButton {
struct ViewModel {
@EnvironmentObject var orderController: OrderController
var buttonText: String {
if orderController.order.items.isEmpty {
return "Your Order"
} else {
return "Your Order $ \(orderController.order.total)"
}
}
}
}
extension OrderDetail {
struct ViewModel {
@EnvironmentObject var orderController: OrderController
var dishList: [Dish] {
return orderController.order.items
}
var totalPrice: Double {
return orderController.order.total
}
}
}
```
#### 2. 测试副作用与 Spy 测试替身
在编写不返回输出而是改变其他对象状态的系统测试时,可以通过监视受被测系统(SUT)影响的对象来进行测试。当无法控制发生副作用的对象时,引入了一种新的测试替身:Spy。
以支付流程为例,选择了 Hippo Payments 作为第三方支付服务,其提供的 Swift SDK 接口如下:
```swift
class HippoPaymentsProcessor {
init(apiKey: String)
func processPayment(
payload: [String : Any],
onSuccess: @escaping () -> Void,
onFailure: @escaping (HippoPaymentsError) -> Void
)
}
```
为了测试点击结账按钮是否启动支付流程,可以创建一个符合 `PaymentProcessing` 协议的 Spy 测试替身:
```swift
// PaymentProcessingSpy.swift
@testable import Albertos
import Combine
class PaymentProcessingSpy: PaymentProcessing {
private(set) var receivedOrder: Order?
func process(order: Order) -> AnyPublisher<Void, Error> {
receivedOrder = order
return Result<Void, Error>.success(()).publisher
.eraseToAnyPublisher()
}
}
```
测试代码如下:
```swift
// OrderDetail.ViewModelTests.swift
func testWhenCheckoutButtonTappedStartsPaymentProcessingFlow() {
let orderController = OrderController()
orderController.addToOrder(item: .fixture(name: "name"))
orderController.addToOrder(item: .fixture(name: "other name"))
let paymentProcessingSpy = PaymentProcessingSpy()
let viewModel = OrderDetail.ViewModel(
orderController: orderController,
paymentProcessor: paymentProcessingSpy
)
viewModel.checkout()
XCTAssertEqual(paymentProcessingSpy.receivedOrder, orderController.order)
}
```
为使测试通过,`OrderDetail.ViewModel` 中的 `checkout` 方法应调用 `process(order:)` 方法:
```swift
// OrderDetail.ViewModel.swift
func checkout() {
paymentProcessor.process(order: orderController.order)
}
```
#### 3. 抽象第三方依赖的好处
抽象第三方依赖有诸多好处,比如更易于从一个供应商迁移到另一个供应商。可以通过定义特定于领域的协议来抽象特定供应商的实现细节,采用不同的选项时只需使其符合该协议即可。
还可以使第三方依赖符合代码库的风格。例如,`HippoPaymentsProcessor` 有两个用于成功和失败处理程序的闭包参数,而代码库使用 `Combine Publishers` 来管理异步工作。可以定义自己的抽象层,并扩展 `HippoPaymentsProcessor` 以使其符合该抽象层:
```swift
// PaymentProcessing.swift
import Combine
protocol PaymentProcessing {
func process(order: Order) -> AnyPublisher<Void, Error>
}
// HippoPaymentsProcessor+PaymentProcessing.swift
import Combine
import HippoPayments
extension HippoPaymentsProcessor: PaymentProcessing {
func process(order: Order) -> AnyPublisher<Void, Error> {
return Future { promise in
self.processPayment(
payload: ???,
```
0
0
复制全文
相关推荐










