FOSMVVM ServerRequest Test Generator
Generate test files for ServerRequest types using VaporTesting infrastructure.
Conceptual Foundation
For full architecture context, see FOSMVVMArchitecture.md | OpenClaw reference
ServerRequest testing uses VaporTesting infrastructure to send typed requests through the full server stack:
CODEBLOCK0
STOP AND READ THIS
Testing ServerRequests uses VaporTesting infrastructure. No manual URL construction. Ever.
CODEBLOCK1
What You Must NEVER Do
CODEBLOCK2
What You Must ALWAYS Do
CODEBLOCK3
The path is derived from the ServerRequest type. HTTP method comes from the action. Headers are automatic. You NEVER write URL strings or decode JSON manually.
When to Use This Skill
- - Testing any ServerRequest implementation
- Verifying server responses for CRUD operations
- Testing error handling and edge cases
- Multi-locale response verification
- Integration testing between client request types and server controllers
If you're about to write URLSession, app.test(.GET, "/path"), or manual JSON decoding, STOP and use this skill instead.
What This Skill Generates
| File | Location | Purpose |
|---|
| INLINECODE2 | INLINECODE3 | Test suite for ServerRequest |
| Test YAML (if needed) |
Tests/{Target}Tests/TestYAML/ | Localization for test ViewModels |
Project Structure Configuration
| Placeholder | Description | Example |
|---|
| INLINECODE5 | Feature or entity name (PascalCase) | INLINECODE6 , User, INLINECODE8 |
| INLINECODE9 |
Server test target |
WebServerTests,
AppTests |
|
{ViewModelsTarget} | Shared ViewModels SPM target |
ViewModels |
|
{WebServerTarget} | Server-side target |
WebServer,
AppServer |
|
{ResourceDir} | YAML resource directory |
TestYAML,
Resources |
Key Types
TestingServerRequestResponse
Wraps HTTP response with typed access:
| Property | Type | Description |
|---|
| INLINECODE20 | INLINECODE21 | HTTP status code (.ok, .notFound, etc.) |
| INLINECODE22 |
HTTPHeaders | Response headers |
|
body |
R.ResponseBody? |
Typed response body (auto-decoded) |
|
error |
R.ResponseError? |
Typed error (auto-decoded) |
TestingApplicationTester Extension
CODEBLOCK4
Convenience Locales
Available on TestingApplicationTester:
- -
en - English - INLINECODE30 - English (US)
- INLINECODE31 - English (UK)
- INLINECODE32 - Spanish
Test Structure
Basic Test Suite
CODEBLOCK5
Testing Different Request Types
| Request Type | HTTP Method | What to Test |
|---|
| INLINECODE33 | GET | Query params, response body, localization |
| INLINECODE34 |
GET | ViewModel population, all localized fields |
|
CreateRequest | POST | RequestBody validation, created entity, ID response |
|
UpdateRequest | PATCH | RequestBody validation, updated entity, response |
|
DeleteRequest | DELETE | Entity removal, status code |
How to Use This Skill
Invocation:
/fosmvvm-serverrequest-test-generator
Prerequisites:
- - ServerRequest type understood from conversation context
- Test scenarios identified (success paths, error paths, validation)
- Controller implementation exists or is being created
- VaporTesting infrastructure understood
Workflow integration:
This skill is used when testing ServerRequest implementations. The skill references conversation context automatically—no file paths or Q&A needed. Typically follows fosmvvm-serverrequest-generator.
Pattern Implementation
This skill references conversation context to determine test structure:
Request Analysis
From conversation context, the skill identifies:
- - ServerRequest type (from prior discussion or server implementation)
- Request protocol (ShowRequest, CreateRequest, UpdateRequest, etc.)
- ResponseBody type (ViewModel or simple structure)
- ResponseError type (custom errors or EmptyError)
Test Scenario Planning
Based on operation semantics:
- - Success paths (valid input, expected output)
- Error paths (not found, validation failure, business logic errors)
- Localization (if ResponseBody has localized fields)
- Multi-locale (testing across supported locales)
Infrastructure Detection
From project state:
- - Existing test patterns (similar test files in codebase)
- Localization setup (YAML fixtures needed)
- Database requirements (seed data for tests)
Test File Generation
- 1. Test suite conforming to VaporTesting patterns
- One @Test function per scenario
- withTestApp helper for application setup
- Route registration
- Request invocations using app.testing().test()
Context Sources
Skill references information from:
- - Prior conversation: Test requirements, scenarios discussed
- ServerRequest: If Claude has read ServerRequest code into context
- Controller: From server implementation
- Existing tests: From codebase analysis of similar test files
Common Scenarios
Testing ViewModelRequest with Localization
CODEBLOCK6
Testing CreateRequest with Validation
CODEBLOCK7
Testing UpdateRequest
CODEBLOCK8
Testing DeleteRequest
CODEBLOCK9
Testing ShowRequest with Query Parameters
CODEBLOCK10
Testing ServerRequestError Localizations
Why Error Localization Testing is Different
Unlike ViewModels, ServerRequestError types:
- - Are often enums, not structs
- Do not conform to
Stubbable or INLINECODE40 - Cannot use
expectTranslations(ErrorType.self) like ViewModels
This means you must manually test each error case individually.
The Pattern
Use LocalizableTestCase.expectTranslations(_ localizable:) on each error's Localizable property:
CODEBLOCK11
Testing Error Messages in Integration Tests
When testing the full request/response cycle, verify error messages resolve:
CODEBLOCK12
Why Not Stubbable?
INLINECODE44 works well for ViewModels because:
- - ViewModels are structs with many properties
- A single
stub() provides a complete test instance
INLINECODE46 types are often enums where:
- - Each case may have different associated values
- Each case may have a different localized message
- A single
stub() can't cover all cases
You must enumerate and test each error case explicitly.
Checklist for Error Localization Tests
- - [ ] Test each enum case for simple errors
- [ ] Test representative associated values for parameterized errors
- [ ] Verify messages resolve (not empty) for all configured locales
- [ ] Verify substitution placeholders are replaced in INLINECODE48
Troubleshooting
"Route not found" Error
Cause: Controller not registered in test app.
Fix: Register the controller before testing:
CODEBLOCK13
Response body is nil but status is .ok
Cause: JSON decoding failed silently.
Fix: Check that ResponseBody type matches server response exactly. Use response.headers to verify Content-Type.
Localization not applied
Cause: Locale not passed to encoder.
Fix: The test(_:locale:) method handles this automatically. Ensure you're passing the locale parameter.
"Missing Translation" in Response
Cause: YAML localization not loaded.
Fix: Initialize localization store in test app setup:
try app.initYamlLocalization(
bundle: Bundle.module,
resourceDirectoryName: "TestYAML"
)
Naming Conventions
| Concept | Convention | Example |
|---|
| Test suite | INLINECODE52 | INLINECODE53 |
| Test file |
{Feature}RequestTests.swift |
IdeaRequestTests.swift |
| Test method (success) |
{action}Request_success |
showRequest_success |
| Test method (error) |
{action}Request_{errorCase} |
showRequest_notFound |
| Test method (validation) |
{action}Request_{validationCase} |
createRequest_emptyContent |
| Test helper |
withTestApp |
withTestApp { app in } |
| Locale constant |
en,
es,
enUS,
enGB |
locale: en |
File Templates
See reference.md for complete file templates.
See Also
Version History
| Version | Date | Changes |
|---|
| 1.1 | 2025-01-20 | Add ServerRequestError localization testing guidance |
| 1.2 |
2026-01-24 | Update to context-aware approach (remove file-parsing/Q&A). Skill references conversation context instead of asking questions or accepting file paths. |
| 1.0 | 2025-01-05 | Initial skill |
FOSMVVM ServerRequest 测试生成器
使用 VaporTesting 基础设施为 ServerRequest 类型生成测试文件。
概念基础
完整架构上下文请参阅 FOSMVVMArchitecture.md | OpenClaw 参考
ServerRequest 测试使用 VaporTesting 基础设施,通过完整的服务器栈发送类型化请求:
┌─────────────────────────────────────────────────────────────────────┐
│ ServerRequest 测试流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 测试代码: │
│ let request = MyRequest(query: .init(...)) │
│ app.testing().test(request, locale: en) { response in } │
│ │
│ 基础设施处理: │
│ • 从类型名称推导路径(MyRequest → /my) │
│ • 从操作类型推导 HTTP 方法(ShowRequest → GET) │
│ • 查询/请求体编码 │
│ • 请求头注入(locale, version) │
│ • 响应解码为 ResponseBody 类型 │
│ │
│ 你需要验证: │
│ • response.status(HTTPStatus) │
│ • response.body(R.ResponseBody? - 类型化!) │
│ • response.error(R.ResponseError? - 类型化!) │
│ │
└─────────────────────────────────────────────────────────────────────┘
请先阅读以下内容
测试 ServerRequest 使用 VaporTesting 基础设施。永远不要手动构建 URL。
┌──────────────────────────────────────────────────────────────────────┐
│ SERVERREQUEST 测试使用 TestingApplicationTester │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 配置包含路由的 Vapor Application │
│ 2. 使用 app.testing().test(request, locale:) { response in } │
│ 3. 验证 response.status、response.body、response.error │
│ │
│ TestingServerRequestResponse 提供类型化访问: │
│ • status: HTTPStatus │
│ • headers: HTTPHeaders │
│ • body: R.ResponseBody? ← 自动解码! │
│ • error: R.ResponseError? ← 自动解码! │
│ │
└──────────────────────────────────────────────────────────────────────┘
绝对不要这样做
swift
// ❌ 错误 - 手动构建 URL
let url = URL(string: http://localhost:8080/my_request?query=value)!
let response = try await URLSession.shared.data(from: url)
// ❌ 错误 - 使用字符串路径和方法
try await app.test(.GET, /my_request) { response in }
// ❌ 错误 - 手动 JSON 编码/解码
let json = try JSONEncoder().encode(requestBody)
let decoded = try JSONDecoder().decode(ResponseBody.self, from: data)
// ❌ 错误 - 手动构建 TestingHTTPRequest
let httpRequest = TestingHTTPRequest(method: .GET, url: /path, headers: headers)
try await app.testing().performTest(request: httpRequest)
始终这样做
swift
// ✅ 正确 - 使用 TestingApplicationTester.test() 配合 ServerRequest
let request = MyShowRequest(query: .init(userId: userId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .ok)
#expect(response.body?.viewModel.name == Expected Name)
}
// ✅ 正确 - 测试多语言环境
for locale in [en, es] {
try await app.testing().test(request, locale: locale) { response in
#expect(response.status == .ok)
// 本地化值会自动处理
}
}
// ✅ 正确 - 测试错误响应
let badRequest = MyShowRequest(query: .init(userId: invalidId))
try await app.testing().test(badRequest, locale: en) { response in
#expect(response.status == .notFound)
#expect(response.error != nil)
}
路径从 ServerRequest 类型推导。HTTP 方法来自操作类型。请求头自动处理。你永远不需要编写 URL 字符串或手动解码 JSON。
何时使用此技能
- - 测试任何 ServerRequest 实现
- 验证 CRUD 操作的服务器响应
- 测试错误处理和边界情况
- 多语言环境响应验证
- 客户端请求类型与服务器控制器之间的集成测试
如果你正准备编写 URLSession、app.test(.GET, /path) 或手动 JSON 解码,请立即停止并使用此技能。
此技能生成的内容
| 文件 | 位置 | 用途 |
|---|
| {Feature}RequestTests.swift | Tests/{Target}Tests/Requests/ | ServerRequest 测试套件 |
| 测试 YAML(如需) |
Tests/{Target}Tests/TestYAML/ | 测试 ViewModel 的本地化 |
项目结构配置
| 占位符 | 描述 | 示例 |
|---|
| {Feature} | 功能或实体名称(帕斯卡命名法) | Idea、User、Dashboard |
| {Target} |
服务器测试目标 | WebServerTests、AppTests |
| {ViewModelsTarget} | 共享 ViewModels SPM 目标 | ViewModels |
| {WebServerTarget} | 服务端目标 | WebServer、AppServer |
| {ResourceDir} | YAML 资源目录 | TestYAML、Resources |
关键类型
TestingServerRequestResponse
封装 HTTP 响应并提供类型化访问:
| 属性 | 类型 | 描述 |
|---|
| status | HTTPStatus | HTTP 状态码(.ok、.notFound 等) |
| headers |
HTTPHeaders | 响应头 |
| body | R.ResponseBody? |
类型化响应体(自动解码) |
| error | R.ResponseError? |
类型化错误(自动解码) |
TestingApplicationTester 扩展
swift
func test(
_ request: R,
locale: Locale = en,
headers: HTTPHeaders = [:],
afterResponse: (TestingServerRequestResponse) async throws -> Void
) async throws -> any TestingApplicationTester
便捷语言环境
TestingApplicationTester 上可用:
- - en - 英语
- enUS - 英语(美国)
- enGB - 英语(英国)
- es - 西班牙语
测试结构
基本测试套件
swift
import FOSFoundation
@testable import FOSMVVM
import FOSTesting
import FOSTestingVapor
import Foundation
import Testing
import Vapor
import VaporTesting
@Suite(MyFeature 请求测试)
struct MyFeatureRequestTests {
@Test func showRequest_成功() async throws {
try await withTestApp { app in
let request = MyShowRequest(query: .init(id: validId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .ok)
#expect(response.body?.viewModel != nil)
}
}
}
@Test func showRequest_未找到() async throws {
try await withTestApp { app in
let request = MyShowRequest(query: .init(id: invalidId))
try await app.testing().test(request, locale: en) { response in
#expect(response.status == .notFound)
}
}
}
}
private func withTestApp(_ test: (Application) async throws -> Void) async throws {
try await withApp { app in
// 配置路由
try app.routes.register(collection: MyController())
try await test(app)
}
}
测试不同请求类型
| 请求类型 | HTTP 方法 | 测试内容 |
|---|
| ShowRequest | GET | 查询参数、响应体、本地化 |
| ViewModelRequest |
GET | ViewModel 填充、所有本地化字段 |
| CreateRequest | POST | RequestBody 验证、创建的实体、ID 响应 |
| UpdateRequest | PATCH | RequestBody 验证、更新的实体、响应 |
| DeleteRequest | DELETE | 实体删除、状态码 |
如何使用此技能
调用方式:
/fosmvvm-serverrequest-test-generator
前置条件:
- - 从对话上下文中理解 ServerRequest 类型
- 已确定测试场景(成功