generate-tests
Skill50 starsChanged 9 months agoApache-2.0View on GitHub
Generate unit and UI tests for specified code with edge cases and mocks. Triggers: "generate tests", "write tests", "add tests", "test coverage".
We split this skill into the rules it gives an agent
7 rules. 0 are common, 7 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 7 edited versions we read keep these.
7
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 27
If uncommitted changes exist:
- Distinctive: line 44
If "Commit first": Ask for a commit message, stage changed files, and commit.
- Distinctive: line 66
If both are present:
- Distinctive: line 117
Read the target code and extract:
- Distinctive: line 140
Before writing tests, create a plan:
- Distinctive: line 297
Write the generated tests to match the existing directory structure and naming conventions.
- Distinctive: line 310
If tests fail, fix the issues before presenting results.
“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Generate Tests
Quick Ref: Generate unit and UI tests with proper mocking and edge case coverage. Outputs directly to Tests/ directory.
YOU MUST EXECUTE THIS WORKFLOW. Do not just describe it.
Pre-flight: Git Safety Check
git status --shortIf uncommitted changes exist:
AskUserQuestion with questions:
[
{
"question": "You have uncommitted changes. Commit before proceeding?",
"header": "Git",
"options": [
{"label": "Commit first (Recommended)", "description": "Save current work so you can revert if this skill modifies files"},
{"label": "Continue without committing", "description": "Proceed — I accept the risk"}
],
"multiSelect": false
}
]If "Commit first": Ask for a commit message, stage changed files, and commit. Then proceed.
Step 1: Detect Test Framework
Auto-detect which testing framework the project uses:
# Check for Swift Testing
Grep pattern="import Testing" glob="**/*Test*.swift" output_mode="count"
# Check for XCTest
Grep pattern="import XCTest" glob="**/*Test*.swift" output_mode="count"| Detection | Framework | Syntax |
import Testing found | Swift Testing | @Test, #expect, @Suite |
Only import XCTest | XCTest | XCTestCase, XCTAssert* |
| New project (no tests) | Swift Testing (Recommended) | Modern, concurrent-safe |
If both are present:
AskUserQuestion with questions:
[
{
"question": "Which testing framework should I use?",
"header": "Framework",
"options": [
{"label": "Swift Testing (Recommended)", "description": "Modern framework with @Test, #expect, better async support"},
{"label": "XCTest", "description": "Classic framework with XCTestCase, XCTAssert*"}
],
"multiSelect": false
}
]Step 2: Gather Test Requirements
AskUserQuestion with questions:
[
{
"question": "What would you like to generate tests for?",
"header": "Target",
"options": [
{"label": "Specific type/file", "description": "I'll provide the name or path"},
{"label": "Analyze coverage gaps", "description": "Find untested code and suggest tests"},
{"label": "Feature area", "description": "Generate tests for a feature (e.g., 'authentication')"}
],
"multiSelect": false
},
{
"question": "What type of tests do you need?",
"header": "Test type",
"options": [
{"label": "Unit tests", "description": "Test individual functions/methods in isolation"},
{"label": "UI tests", "description": "Test user interactions and flows"},
{"label": "All applicable", "description": "Generate appropriate tests for each layer"}
],
"multiSelect": true
}
]Step 3: Analyze Target Code
Read the target code and extract:
- Public API Surface — public/internal functions, initializers, computed properties
- Dependencies — protocol dependencies (need mocks), external services (need stubs)
- Side Effects — network calls, file I/O, UserDefaults, notifications
- State Transitions — how internal state changes, what triggers transitions
# Find the target file
Glob pattern="**/*TargetName*.swift"
# Read the target
Read file_path="<path_to_target>"
# Find existing tests for this target
Glob pattern="**/*TargetName*Tests.swift"
Glob pattern="**/*TargetName*Test.swift"Step 4: Generate Test Plan
Before writing tests, create a plan:
## Test Plan for [TargetType]
### Unit Tests Needed
| Test Case | Input | Expected Output | Priority |
|-----------|-------|-----------------|----------|
| init_withValidData_succeeds | Valid params | Instance created | High |
| init_withMissingRequired_throws | nil required | ValidationError | High |
| fetch_whenEmpty_returnsEmptyArray | Mock empty | [] | Medium |
### Mocks Required
| Protocol | Mock Name | Behavior to Simulate |
|----------|-----------|---------------------|
| NetworkService | MockNetworkService | Success, failure, timeout |
### Edge Cases
| Scenario | Test Name |
|----------|-----------|
| Empty input | test_withEmptyInput_handlesGracefully |
| Nil optional | test_withNilOptional_usesDefault |
| Concurrent access | test_concurrentCalls_threadSafe |Step 5: Generate Mock Implementations
For each protocol dependency, generate a mock:
@testable import YourApp
final class MockNetworkService: NetworkServiceProtocol {
var fetchResult: Result<[Item], Error> = .success([])
var fetchCallCount = 0
func fetch<T: Decodable>(from url: URL) async throws -> T {
fetchCallCount += 1
switch fetchResult {
case .success(let data):
guard let result = data as? T else {
throw MockError.typeMismatch
}
return result
case .failure(let error):
throw error
}
}
enum MockError: Error {
case typeMismatch
}
}Step 6: Generate Tests
Swift Testing Format
import Testing
@testable import YourApp
@Suite("ItemViewModel Tests")
struct ItemViewModelTests {
@Test("Initialize with valid data succeeds")
func init_withValidData_succeeds() throws {
let viewModel = ItemViewModel(item: .preview)
#expect(viewModel.title == "Preview Item")
#expect(viewModel.isLoading == false)
}
@Test("Fetch items when network available returns items")
func fetch_whenNetworkAvailable_returnsItems() async throws {
let mockService = MockNetworkService()
mockService.fetchResult = .success([Item.preview])
let viewModel = ItemViewModel(networkService: mockService)
try await viewModel.fetchItems()
#expect(viewModel.items.count == 1)
}
@Test("Fetch items when network fails throws error")
func fetch_whenNetworkFails_throwsError() async {
let mockService = MockNetworkService()
mockService.fetchResult = .failure(NetworkError.noConnection)
let viewModel = ItemViewModel(networkService: mockService)
await #expect(throws: NetworkError.self) {
try await viewModel.fetchItems()
}
}
}XCTest Format
import XCTest
@testable import YourApp
final class ItemViewModelTests: XCTestCase {
var sut: ItemViewModel!
var mockService: MockNetworkService!
override func setUp() {
super.setUp()
mockService = MockNetworkService()
sut = ItemViewModel(networkService: mockService)
}
override func tearDown() {
sut = nil
mockService = nil
super.tearDown()
}
func test_init_withValidData_succeeds() {
let viewModel = ItemViewModel(item: .preview)
XCTAssertEqual(viewModel.title, "Preview Item")
XCTAssertFalse(viewModel.isLoading)
}
func test_fetch_whenNetworkFails_throwsError() async {
mockService.stubbedFetchResult = .failure(NetworkError.noConnection)
do {
try await sut.fetchItems()
XCTFail("Expected error to be thrown")
} catch {
XCTAssertTrue(error is NetworkError)
}
}
}Step 7: Write Tests to Project
Determine the correct test file location:
# Find existing test directory structure
Glob pattern="**/*Tests.swift"
# Common patterns:
# Tests/AppTests/ViewModels/ItemViewModelTests.swift
# Tests/UnitTests/ItemViewModelTests.swiftWrite the generated tests to match the existing directory structure and naming conventions.
Step 8: Verify and Present Summary
8.1: Run Tests
# Verify generated tests compile and pass
xcodebuild test -scheme <SCHEME> -destination 'platform=iOS Simulator,name=iPhone 16' -only-testing:<TEST_TARGET> -quiet 2>&1 | tail -10If tests fail, fix the issues before presenting results.
8.2: Display Results
Display the summary inline so the user sees results immediately:
## Test Generation Complete
**Target:** ItemViewModel.swift
**Framework:** Swift Testing
**Tests Generated:** N
**Status:** All passing ✓
| Test Type | Count | File |
|-----------|-------|------|
| Unit Tests | 10 | Tests/ItemViewModelTests.swift |
| Mock | 1 | Tests/Mocks/MockNetworkService.swift |
**Coverage Added:**
- Initialization: 2 tests
- Fetch operations: 3 tests
- Error handling: 2 tests
- Edge cases: 3 testsTest Naming Convention
Follow: [method]_[scenario]_[expectedBehavior]
Examples:
fetchItems_whenNetworkAvailable_returnsItemssaveItem_withEmptyTitle_throwsValidationErrorinit_withNilOptional_usesDefaultValuedelete_whenItemExists_removesFromList
Common Test Patterns
Parameterized Tests (Swift Testing)
@Test(arguments: [
("valid@email.com", true),
("invalid", false),
("", false),
("a@b.c", true)
])
func validateEmail_withInput_returnsExpected(email: String, expected: Bool) {
#expect(sut.isValidEmail(email) == expected)
}Testing @MainActor Code
@Test @MainActor func updateUI_onMainThread() {
sut.updateTitle("New Title")
#expect(sut.displayTitle == "New Title")
}Testing Errors (Swift Testing)
@Test func invalidInput_throwsValidationError() async {
await #expect(throws: ValidationError.self) {
try await sut.validate(input: "")
}
}Troubleshooting
| Problem | Solution |
| Can't find test directory | Check for Tests/ or *Tests/ at project root |
| Target has no protocol dependencies | Test public methods directly without mocks |
| @MainActor prevents test creation | Add @MainActor to test function or use MainActor.run {} |
| SwiftData ModelContext in tests | Create in-memory container: ModelContainer(for:, configurations: .init(isStoredInMemoryOnly: true)) |
| Tests can't import app module | Verify test target has the app module in its dependencies |
Other versions7 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 7
- coco-research/coco★ 797same name
- hidai25/eval-view★ 140same name
- mavka-ai/unit-tests-skills★ 55same name
- zbruhnke/claude-code-starter★ 16same name
- chidekina/aria-superpowers★ 2same name
- simu5116/ClaudeCode★ 0same name
- toantran292/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository4 other files it gives its agents.
- plain-talkSkill
- release-prepSkill
- safe-refactorSkill
- tech-talkSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

