test-writer

Aura Frog Test Writer

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "test-writer" with this command: npx skills add nguyenthienthanh/aura-frog/nguyenthienthanh-aura-frog-test-writer

Aura Frog Test Writer

Priority: MEDIUM - Use for test-related requests Version: 1.0.0

When to Use

USE for:

  • Adding tests to existing code

  • Improving test coverage

  • Creating test suites

  • TDD implementation (Phase 5a)

  • Writing specific test types (unit, integration, E2E)

DON'T use for:

  • Bug fixes without explicit test request → use bugfix-quick

  • Full feature implementation → use workflow-orchestrator

Test Writing Process

  1. Analyze Target Code

  2. Read file with Read tool

  3. Identify testable units:

    • Functions/methods
    • Components
    • API endpoints
    • Data transformations
  4. List dependencies to mock

  5. Plan Strategy

Type Use For Scope

Unit Individual functions/components Single unit, mocked deps

Integration Module interactions, API calls Multiple units together

E2E Complete user flows Full system, real deps

  1. Write Tests

For NEW code (TDD - Phase 5a):

  1. Write failing tests (RED) → Tests MUST fail → If they pass, tests are wrong
  2. Implement code (GREEN) → Minimal code to pass
  3. Refactor (REFACTOR) → Tests must stay green

For EXISTING code:

  1. Write tests that pass (validate current behavior)

  2. Add edge case tests

  3. Add negative tests (error handling)

  4. Verify Coverage

Check target: 80% or project-specific

npm test -- --coverage # JavaScript/TypeScript pytest --cov=. --cov-report=html # Python ./vendor/bin/phpunit --coverage-html coverage # PHP go test -coverprofile=coverage.out ./... # Go

Framework-Specific Templates

JavaScript/TypeScript - Jest

Unit Test (Function):

describe('calculateDiscount', () => { it('should apply 10% discount for orders over $100', () => { expect(calculateDiscount(150)).toBe(135); });

it('should return original price for orders under $100', () => { expect(calculateDiscount(50)).toBe(50); });

it('should throw error for negative amounts', () => { expect(() => calculateDiscount(-10)).toThrow('Invalid amount'); }); });

Unit Test (React Component):

import { render, fireEvent, screen } from '@testing-library/react'; import { LoginButton } from './LoginButton';

describe('LoginButton', () => { it('should call onLogin when clicked', () => { const onLogin = jest.fn(); render(<LoginButton onLogin={onLogin} />);

fireEvent.click(screen.getByRole('button', { name: /login/i }));

expect(onLogin).toHaveBeenCalledTimes(1);

});

it('should show loading state', () => { render(<LoginButton isLoading={true} />);

expect(screen.getByTestId('loading-spinner')).toBeVisible();

});

it('should be disabled when loading', () => { render(<LoginButton isLoading={true} />);

expect(screen.getByRole('button')).toBeDisabled();

}); });

React Native - Jest + React Native Testing Library

Component Test:

import { render, fireEvent } from '@testing-library/react-native'; import { PaymentCard } from './PaymentCard';

describe('PaymentCard', () => { it('should display amount in correct format', () => { const { getByTestId } = render(<PaymentCard amount={1500.50} />);

expect(getByTestId('amount-display')).toHaveTextContent('$1,500.50');

});

it('should call onPay when pay button pressed', () => { const onPay = jest.fn(); const { getByTestId } = render(<PaymentCard onPay={onPay} />);

fireEvent.press(getByTestId('pay-button'));

expect(onPay).toHaveBeenCalledTimes(1);

}); });

React Native - Detox E2E

E2E Test:

describe('Login Flow', () => { beforeAll(async () => { await device.launchApp(); });

beforeEach(async () => { await device.reloadReactNative(); });

it('should login successfully with valid credentials', async () => { await element(by.id('email-input')).typeText('user@example.com'); await element(by.id('password-input')).typeText('securePassword123'); await element(by.id('login-button')).tap();

await expect(element(by.id('home-screen'))).toBeVisible();
await expect(element(by.text('Welcome'))).toBeVisible();

});

it('should show error for invalid credentials', async () => { await element(by.id('email-input')).typeText('wrong@email.com'); await element(by.id('password-input')).typeText('wrongpass'); await element(by.id('login-button')).tap();

await expect(element(by.text('Invalid credentials'))).toBeVisible();

}); });

PHP - PHPUnit

Unit Test:

<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase; use App\Services\OrderCalculator;

class OrderCalculatorTest extends TestCase { private OrderCalculator $calculator;

protected function setUp(): void
{
    $this->calculator = new OrderCalculator();
}

public function test_calculates_subtotal_correctly(): void
{
    $items = [
        ['price' => 100, 'quantity' => 2],
        ['price' => 50, 'quantity' => 1],
    ];

    $result = $this->calculator->calculateSubtotal($items);

    $this->assertEquals(250, $result);
}

public function test_applies_discount_percentage(): void
{
    $result = $this->calculator->applyDiscount(100, 10);

    $this->assertEquals(90, $result);
}

public function test_throws_exception_for_negative_discount(): void
{
    $this->expectException(\InvalidArgumentException::class);
    $this->expectExceptionMessage('Discount cannot be negative');

    $this->calculator->applyDiscount(100, -5);
}

}

Laravel Feature Test:

<?php

namespace Tests\Feature;

use Tests\TestCase; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase;

class AuthenticationTest extends TestCase { use RefreshDatabase;

public function test_user_can_login_with_correct_credentials(): void
{
    $user = User::factory()->create([
        'email' => 'test@example.com',
        'password' => bcrypt('password123'),
    ]);

    $response = $this->postJson('/api/login', [
        'email' => 'test@example.com',
        'password' => 'password123',
    ]);

    $response->assertStatus(200)
             ->assertJsonStructure(['token', 'user']);
}

public function test_user_cannot_login_with_wrong_password(): void
{
    $user = User::factory()->create([
        'email' => 'test@example.com',
        'password' => bcrypt('password123'),
    ]);

    $response = $this->postJson('/api/login', [
        'email' => 'test@example.com',
        'password' => 'wrongpassword',
    ]);

    $response->assertStatus(401)
             ->assertJson(['message' => 'Invalid credentials']);
}

}

Python - PyTest

Unit Test:

import pytest from app.services.calculator import OrderCalculator

class TestOrderCalculator: @pytest.fixture def calculator(self): return OrderCalculator()

def test_calculate_subtotal(self, calculator):
    items = [
        {"price": 100, "quantity": 2},
        {"price": 50, "quantity": 1},
    ]

    result = calculator.calculate_subtotal(items)

    assert result == 250

def test_apply_discount_percentage(self, calculator):
    result = calculator.apply_discount(100, 10)

    assert result == 90

def test_raises_error_for_negative_discount(self, calculator):
    with pytest.raises(ValueError, match="Discount cannot be negative"):
        calculator.apply_discount(100, -5)

@pytest.mark.parametrize("amount,discount,expected", [
    (100, 0, 100),
    (100, 10, 90),
    (100, 50, 50),
    (100, 100, 0),
])
def test_discount_edge_cases(self, calculator, amount, discount, expected):
    assert calculator.apply_discount(amount, discount) == expected

FastAPI Integration Test:

import pytest from fastapi.testclient import TestClient from app.main import app from app.models import User

@pytest.fixture def client(): return TestClient(app)

@pytest.fixture def test_user(db_session): user = User(email="test@example.com", password="hashed_password") db_session.add(user) db_session.commit() return user

class TestAuthenticationAPI: def test_login_success(self, client, test_user): response = client.post("/api/login", json={ "email": "test@example.com", "password": "password123" })

    assert response.status_code == 200
    assert "token" in response.json()
    assert "user" in response.json()

def test_login_wrong_password(self, client, test_user):
    response = client.post("/api/login", json={
        "email": "test@example.com",
        "password": "wrongpassword"
    })

    assert response.status_code == 401
    assert response.json()["detail"] == "Invalid credentials"

def test_login_missing_fields(self, client):
    response = client.post("/api/login", json={})

    assert response.status_code == 422

Go - Go Testing

Unit Test:

package calculator

import ( "testing" )

func TestCalculateSubtotal(t *testing.T) { calc := NewOrderCalculator() items := []Item{ {Price: 100, Quantity: 2}, {Price: 50, Quantity: 1}, }

result := calc.CalculateSubtotal(items)

if result != 250 {
    t.Errorf("Expected 250, got %d", result)
}

}

func TestApplyDiscount(t *testing.T) { calc := NewOrderCalculator()

result := calc.ApplyDiscount(100, 10)

if result != 90 {
    t.Errorf("Expected 90, got %d", result)
}

}

func TestApplyDiscountNegative(t *testing.T) { calc := NewOrderCalculator()

defer func() {
    if r := recover(); r == nil {
        t.Errorf("Expected panic for negative discount")
    }
}()

calc.ApplyDiscount(100, -5)

}

// Table-driven test func TestApplyDiscountEdgeCases(t *testing.T) { calc := NewOrderCalculator()

tests := []struct {
    name     string
    amount   int
    discount int
    expected int
}{
    {"no discount", 100, 0, 100},
    {"10% discount", 100, 10, 90},
    {"50% discount", 100, 50, 50},
    {"full discount", 100, 100, 0},
}

for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        result := calc.ApplyDiscount(tt.amount, tt.discount)
        if result != tt.expected {
            t.Errorf("Expected %d, got %d", tt.expected, result)
        }
    })
}

}

Coverage Targets

Type Target Rationale

Critical paths 100% Auth, payment, security

Business logic 90% Core domain logic

UI/Utilities 80% User-facing components

Overall 80% Project minimum (or custom)

Test File Naming Conventions

Framework Test File Location

Jest *.test.ts , *.spec.ts

tests/ or alongside

PHPUnit *Test.php

tests/Unit/ , tests/Feature/

PyTest test_*.py , *_test.py

tests/

Go *_test.go

Same package

Detox *.e2e.ts

e2e/

Cypress *.cy.ts

cypress/e2e/

Running Tests by Framework

JavaScript/TypeScript (Jest)

npm test npm test -- --coverage npm test -- --watch

PHP (PHPUnit)

./vendor/bin/phpunit ./vendor/bin/phpunit --coverage-html coverage ./vendor/bin/phpunit --filter TestClassName

Python (PyTest)

pytest pytest --cov=. --cov-report=html pytest -k "test_function_name"

Go

go test ./... go test -v ./... go test -coverprofile=coverage.out ./...

React Native (Detox)

detox test --configuration ios.sim.debug detox test --configuration android.emu.debug

Remember:

  • Tests are documentation - write clear, maintainable tests

  • Follow AAA pattern: Arrange, Act, Assert

  • One assertion concept per test (can have multiple expects)

  • Test behavior, not implementation

  • Mock external dependencies

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

stitch-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

angular-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

visual-pixel-perfect

No summary provided by upstream source.

Repository SourceNeeds Review