Universal testing
Language-agnostic TDD meta-skill. Inject runtime parameters (runtime, test_runner, mock_library, coverage_tool, coverage_gate) to activate language-specific idioms while keeping the core RED → GREEN → REFACTOR philosophy, assertion patterns, and testing tiers constant. Incorporates former: python-testing, kotlin-testing, rust-testing, cpp-testing, golang-testing, react-testing, csharp-testing, fsharp-testing, perl-testing.From its SKILL.md
npx -y skills add asong56/skills --skill universal-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.2 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
universal-testing
Why This Skill Exists
Nine language-specific testing skills (python-testing, kotlin-testing,
rust-testing, cpp-testing, golang-testing, react-testing,
csharp-testing, fsharp-testing, perl-testing) shared an identical
philosophical core — TDD cycle, assertion strategy, testing tiers, coverage
gate — and differed only in tooling names. Per the Three-Principle Framework:
- Output of language-detection = unique input of per-language test skill, with no other consumer → sequential, single-path workflow → MERGE.
- Combined token budget (core + one active config block) stays well under 5 K.
test-driven-development (universal/systematic-debugging) remains independent
per the Circuit-Breaker rules: it operates under different state transitions
and would balloon the prompt past 8 K if merged here.
Runtime Parameters
Call this skill by declaring params at the top of your request or by having the orchestrator inject them:
{
"skill": "universal-testing",
"params": {
"runtime": "Python",
"test_runner": "pytest",
"mock_library": "pytest-mock / unittest.mock",
"coverage_tool": "pytest-cov",
"coverage_gate": "80%"
}
}
Quick-reference param table
runtime | test_runner | mock_library | coverage_tool | Coverage default |
|---|---|---|---|---|
| Python | pytest | pytest-mock / unittest.mock | pytest-cov | 80% |
| Kotlin | Kotest | MockK | Kover | 80% |
| Rust | cargo test / nextest | mockall | cargo-llvm-cov | 80% |
| C++ | GoogleTest / CTest | GoogleMock | gcov / llvm-cov | 80% |
| Go | go test | testify/mock | go tool cover | 80% |
| React/TS | Vitest or Jest + RTL | msw | v8 / istanbul | 80% |
| C# | xUnit | NSubstitute / Moq | Coverlet | 80% |
| F# | xUnit + FsUnit | NSubstitute | Coverlet | 80% |
| Perl | prove + Test2::V0 | Test::MockModule | Devel::Cover | 80% |
If runtime is not provided, infer it from the file extension or import
statements in the code under review. If still ambiguous, ask before proceeding.
Core Philosophy (Runtime-Invariant)
These rules apply regardless of language:
- Test behavior, not implementation. Tests assert outcomes observable from the public API — not internal state, private methods, or call counts unless the call itself is the contract.
- Isolate the unit. Use the declared
mock_libraryto replace all collaborators (I/O, network, time, randomness) so tests are deterministic and fast. - Coverage gate is a floor, not a goal. Hitting
coverage_gatewith trivial assertions is worse than 60% coverage with meaningful assertions. Critical paths require 100% branch coverage regardless of the gate. - Tests are first-class code. Apply the same naming, clarity, and refactoring standards as production code.
The RED → GREEN → REFACTOR Cycle
┌─────────────────────────────────────────────────────────┐
│ 1. RED Write a failing test that expresses the │
│ desired behavior. Run it; confirm it fails │
│ for the right reason (not a syntax error). │
│ ↓ │
│ 2. GREEN Write the minimum code to make the test │
│ pass. Resist the urge to generalize yet. │
│ ↓ │
│ 3. REFACTOR Improve the implementation while keeping │
│ all tests green. Remove duplication, │
│ clarify names, extract concepts. │
│ ↓ │
│ 4. REPEAT Move to the next behavior unit. │
└─────────────────────────────────────────────────────────┘
Testing Tiers
Apply these tiers in every runtime. The ratio guideline is the Testing Pyramid: many unit → fewer integration → few E2E.
Tier 1 — Unit Tests
- Test a single function / method / class in complete isolation.
- All external collaborators are replaced with
mock_librarydoubles. - Must run in < 1 ms each; no network, no filesystem, no clock.
- Naming:
test_<unit>_<scenario>_<expected_outcome>(adapt to language convention — e.g. Go usesTestUnitScenario, Kotlin uses"unit - scenario"in Kotest).
Tier 2 — Integration Tests
- Test the interaction between two or more real components (e.g., service + real database via Testcontainers, or HTTP client + real server on localhost).
- Use factories or fixtures to build test data; never depend on pre-seeded production data.
- Acceptable runtime: seconds, not minutes. Parallelize where safe.
Tier 3 — Contract / API Tests
- Assert the public interface contract of a module or service.
- Provider-side: verify your outputs match consumer expectations.
- Consumer-side: verify you handle all provider response shapes.
Tier 4 — End-to-End / UI Tests
- Exercise the full stack through a browser or CLI entry point.
- Reserve for critical user journeys only (login, checkout, core happy path).
- Treat flakiness as a P1 defect: flaky E2E tests erode trust in the entire suite.
Language-Specific Activation Blocks
Load only the block matching the active runtime. Ignore all others.
🐍 Python (pytest)
# Run all tests with coverage
pytest --cov={package} --cov-report=term-missing --cov-report=html
# Run a single test file
pytest tests/unit/test_service.py -v
# Run with parallel execution
pytest -n auto
Fixture pattern:
import pytest
@pytest.fixture
def user_service(mocker):
repo = mocker.Mock(spec=UserRepository)
return UserService(repo)
def test_find_user_returns_none_when_not_found(user_service, mocker):
mocker.patch.object(user_service._repo, "get", return_value=None)
assert user_service.find("[email protected]") is None
Parametrize pattern:
@pytest.mark.parametrize("email,valid", [
("[email protected]", True),
("bad-email", False),
("", False),
])
def test_email_validation(email, valid):
assert validate_email(email) == valid
Coverage gate command:
pytest --cov={package} --cov-fail-under=80
🟣 Kotlin (Kotest + MockK)
// FunSpec example (preferred for unit tests)
class UserServiceTest : FunSpec({
val repo = mockk<UserRepository>()
val service = UserService(repo)
test("returns null when user not found") {
every { repo.findByEmail("[email protected]") } returns null
service.find("[email protected]") shouldBe null
}
})
Coroutine testing:
test("suspends until result is ready") {
coEvery { repo.fetchAsync(any()) } returns User("alice")
runTest { service.fetchUser("alice") shouldBe User("alice") }
}
Coverage gate command:
./gradlew koverVerify # fails build if < 80%
🦀 Rust (cargo test + mockall)
#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
#[test]
fn returns_error_on_missing_user() {
let mut repo = MockUserRepository::new();
repo.expect_find().with(eq("ghost")).returning(|_| None);
let svc = UserService::new(repo);
assert!(svc.get_user("ghost").is_err());
}
}
Async test:
#[tokio::test]
async fn fetch_user_resolves() {
// ...
}
Coverage gate command:
cargo llvm-cov --fail-under-lines 80
⚙️ C++ (GoogleTest + GoogleMock)
// CMakeLists.txt: add_executable(tests ...) + target_link_libraries(tests GTest::gtest_main gmock)
TEST(UserServiceTest, ReturnsFalseWhenUserNotFound) {
MockUserRepository repo;
EXPECT_CALL(repo, FindByEmail("[email protected]")).WillOnce(Return(std::nullopt));
UserService svc{repo};
EXPECT_FALSE(svc.Find("[email protected]").has_value());
}
Parametrized test:
INSTANTIATE_TEST_SUITE_P(
EmailValidation, EmailTest,
testing::Values(
std::make_tuple("[email protected]", true),
std::make_tuple("bad", false)
)
);
Coverage gate command:
cmake -DCMAKE_BUILD_TYPE=Coverage .. && make && ctest
lcov --summary coverage.info | grep "lines" # manual check against gate
🐹 Go (go test + testify)
func TestUserService_Find_ReturnsNilWhenMissing(t *testing.T) {
repo := new(MockUserRepository)
repo.On("FindByEmail", "[email protected]").Return(nil, nil)
svc := NewUserService(repo)
result, err := svc.Find("[email protected]")
assert.NoError(t, err)
assert.Nil(t, result)
}
Table-driven pattern (idiomatic Go):
tests := []struct{ email string; want bool }{
{"[email protected]", true},
{"bad", false},
}
for _, tc := range tests {
t.Run(tc.email, func(t *testing.T) {
assert.Equal(t, tc.want, ValidateEmail(tc.email))
})
}
Coverage gate command:
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out | grep total # check vs gate manually
⚛️ React / TypeScript (Vitest + RTL + MSW)
// Test what the user sees, not implementation details
test("shows error message on login failure", async () => {
server.use(http.post("/api/login", () => HttpResponse.json({ error: "Bad credentials" }, { status: 401 })));
render(<LoginForm />, { wrapper: Providers });
await userEvent.type(screen.getByLabelText(/email/i), "[email protected]");
await userEvent.click(screen.getByRole("button", { name: /log in/i }));
expect(await screen.findByRole("alert")).toHaveTextContent(/bad credentials/i);
});
Accessibility assertion:
test("form has no accessibility violations", async () => {
const { container } = render(<LoginForm />, { wrapper: Providers });
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Coverage gate command:
vitest run --coverage --coverage.thresholds.lines=80
🔷 C# (xUnit + NSubstitute + Coverlet)
public class UserServiceTests
{
[Fact]
public async Task FindAsync_ReturnsNull_WhenUserNotFound()
{
var repo = Substitute.For<IUserRepository>();
repo.FindByEmailAsync("[email protected]").Returns((User?)null);
var svc = new UserService(repo);
var result = await svc.FindAsync("[email protected]");
result.Should().BeNull();
}
[Theory]
[InlineData("[email protected]", true)]
[InlineData("bad", false)]
public void ValidateEmail_ReturnsExpected(string email, bool expected) =>
EmailValidator.IsValid(email).Should().Be(expected);
}
Coverage gate command:
dotnet test /p:CollectCoverage=true /p:Threshold=80
🟦 F# (xUnit + FsUnit + FsCheck)
[<Fact>]
let ``find returns None when user missing`` () =
let repo = Substitute.For<IUserRepository>()
repo.FindByEmail("[email protected]").Returns(None) |> ignore
let svc = UserService(repo)
svc.Find("[email protected]") |> should equal None
// Property-based test with FsCheck
[<Property>]
let ``non-empty strings are never equal to None result`` (email: NonEmptyString) =
// ...
🐪 Perl (Test2::V0 + prove)
use Test2::V0;
use Test::MockModule;
my $mock = mock 'UserRepository' => (
override => [find_by_email => sub { undef }],
);
my $svc = UserService->new(repo => UserRepository->new);
is $svc->find("ghost\@x.com"), undef, "returns undef when user not found";
done_testing;
Coverage gate command:
cover -test -report html
# Check Devel::Cover output for statement coverage >= 80%
Workflow Checklist
Before marking a TDD cycle complete, verify:
- Test name clearly describes scenario and expected outcome
- Test fails (RED) before implementation is written
- Test passes (GREEN) with minimal implementation
- No implementation details leaked into assertions
- All external I/O is mocked / stubbed
- Edge cases covered: null/empty input, boundary values, error paths
-
coverage_toolrun; gate met or gap documented with rationale - Test is deterministic: passes identically on first and hundredth run
Migration Notes
| Deprecated skill | Replaced by | runtime param |
|---|---|---|
python-testing | universal-testing | "Python" |
kotlin-testing | universal-testing | "Kotlin" |
rust-testing | universal-testing | "Rust" |
cpp-testing | universal-testing | "C++" |
golang-testing | universal-testing | "Go" |
react-testing | universal-testing | "React/TS" |
csharp-testing | universal-testing | "C#" |
fsharp-testing | universal-testing | "F#" |
perl-testing | universal-testing | "Perl" |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.