Memstack Development Test Writer logo

Memstack Development Test Writer

Community
cwinvestments
memstack-development-test-writer

Use this skill when the user says 'write tests', 'add tests', 'test coverage', 'unit tests', 'integration tests', 'component tests', 'mocking', 'edge cases', or needs to generate tests with proper mocking and edge case coverage. Do NOT use for refactoring plans or database migrations.

Overview

Publishercwinvestments
Repositorymemstack
Skill namememstack-development-test-writer
Stars
425
Forks
45
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by cwinvestments on GitHub. Read the source before you install it.

Installation

Install the Memstack Development Test Writer AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/cwinvestments/memstack/tree/master/skills/development/test-writer
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/cwinvestments/memstack.git /tmp/memstack
mkdir -p .claude/skills
cp -r /tmp/memstack/skills/development/test-writer .claude/skills/memstack-development-test-writer
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Memstack Development Test Writer in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Memstack Development Test Writer on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Memstack Development Test Writer is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Test Writer: Generating test suite...

Generates comprehensive test suites with unit, integration, and e2e tests, proper mocking strategies, edge case coverage, naming conventions, and CI integration patterns.

Activation

When this skill activates, output:

Test Writer: Generating test suite...

Then execute the protocol below.

Context Guard

ContextStatus
User says "write tests", "add tests", "test coverage"ACTIVE
User says "unit tests", "integration tests", "component tests", "e2e tests"ACTIVE
User says "mocking", "edge cases", "test this function"ACTIVE
User wants to refactor existing codeDORMANT: use Refactor Planner
User wants to change database schemaDORMANT: use Migration Planner

Common Mistakes

MistakeWhy It's Wrong
"Testing implementation, not behavior"Tests that break when you refactor internals are brittle. Test WHAT it does, not HOW.
"No edge cases"Happy path tests catch 20% of bugs. Boundaries, nulls, empty inputs, and error paths catch the rest.
"Mocking everything"Over-mocking creates tests that pass but don't catch real bugs. Mock boundaries, not internals.
"Test names like test1, test2"Names should describe behavior: "returns empty array when no items match filter". Self-documenting tests.
"No test isolation"Tests that depend on each other or shared state create flaky test suites that nobody trusts.

Protocol

Step 1: Gather Test Requirements

If the user hasn't provided details, ask:

  1. Target: what code needs tests? (function, class, module, API endpoint)
  2. Language/framework: what tech stack? (JS/TS + Jest/Vitest, Python + pytest, Go, etc.)
  3. Test level: unit, integration, e2e, or all three?
  4. Current coverage: any existing tests? What percentage?
  5. Priority: critical paths first, or comprehensive coverage?

Step 2: Analyze the Code Under Test

Before writing tests, understand the target:

Function/method analysis:

PropertyValue
Inputs[Parameters, types, optional/required]
Outputs[Return type, side effects, thrown exceptions]
Dependencies[External calls: DB, API, file system, other modules]
State changes[What mutates: database records, cache, global state]
Branching paths[Number of if/else/switch branches]
Error conditions[What can fail and how it's handled]

Dependency map:

[Target Function]
  ├── [Dependency 1]: mock? [Yes: external / No: pure]
  ├── [Dependency 2]: mock? [Yes: external / No: pure]
  └── [Dependency 3]: mock? [Yes: external / No: pure]

Mock decision rule:

  • Mock external boundaries: databases, APIs, file system, network, time
  • Don't mock pure functions, internal utilities, simple data transformations
  • Sometimes mock sibling modules (mock for unit tests, real for integration)

Step 3: Design Test Cases

Coverage strategy: identify all test scenarios:

Happy path tests:

#ScenarioInputExpected Output
1[Normal case with typical input][Input][Output]
2[Normal case with different valid input][Input][Output]

Edge case tests:

#CategoryScenarioInputExpected Output
1BoundariesMinimum valid value[Input][Output]
2BoundariesMaximum valid value[Input][Output]
3BoundariesExactly at limit[Input][Output]
4EmptyEmpty string / array / object"" / [] / {}[Output]
5Null/UndefinedNull inputnull[Output or error]
6Type coercionUnexpected type"123" vs 123[Output]
7Large inputVery large dataset[X items][Output / perf]
8UnicodeSpecial characters"café ñ 中文"[Output]
9ConcurrentRace condition scenario[Parallel calls][Consistent state]

Error path tests:

#Error ConditionInputExpected Behavior
1Invalid input[Bad input]Throws [ErrorType] with message
2Dependency failure[DB timeout]Returns fallback or propagates error
3Permission denied[Unauthorized]Returns 403 / throws AuthError
4Not found[Missing resource]Returns null or throws NotFoundError

Step 4: Write the Tests

Test file naming conventions:

FrameworkConventionExample
Jest / Vitest[name].test.ts or [name].spec.tsuserService.test.ts
pytesttest_[name].pytest_user_service.py
Go[name]_test.gouser_service_test.go
JUnit[Name]Test.javaUserServiceTest.java

Test naming conventions:

StylePatternExample
Behavior-drivenshould [expected] when [condition]should return empty array when no items match filter
Given-When-Thengiven [state] when [action] then [result]given expired token when authenticating then throws AuthError
Method-focused[method], [scenario], [expected]calculateTotal, with discount: applies percentage reduction

Test structure template (JavaScript/TypeScript):

typescript
describe('[ModuleName]', () => {
  // Setup shared across tests in this block
  let dependency: MockType;

  beforeEach(() => {
    dependency = createMock();
    // Reset state before each test
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  describe('[methodName]', () => {
    // Happy path
    it('should [expected behavior] when [normal condition]', () => {
      // Arrange
      const input = { /* test data */ };
      dependency.method.mockResolvedValue(expectedData);

      // Act
      const result = targetMethod(input);

      // Assert
      expect(result).toEqual(expectedOutput);
    });

    // Edge case
    it('should [expected behavior] when [edge condition]', () => {
      // Arrange
      const input = { /* edge case data */ };

      // Act
      const result = targetMethod(input);

      // Assert
      expect(result).toEqual(edgeCaseOutput);
    });

    // Error path
    it('should throw [ErrorType] when [error condition]', () => {
      // Arrange
      const input = { /* invalid data */ };

      // Act & Assert
      expect(() => targetMethod(input)).toThrow(ErrorType);
    });
  });
});

Test structure template (Python):

python
import pytest
from unittest.mock import Mock, patch

class TestModuleName:
    """Tests for [ModuleName]."""

    def setup_method(self):
        """Reset state before each test."""
        self.dependency = Mock()

    # Happy path
    def test_method_returns_expected_when_normal_input(self):
        """Should [expected behavior] when [condition]."""
        # Arrange
        input_data = {"key": "value"}
        self.dependency.method.return_value = expected_data

        # Act
        result = target_method(input_data)

        # Assert
        assert result == expected_output

    # Edge case
    def test_method_handles_empty_input(self):
        """Should [expected behavior] when input is empty."""
        result = target_method([])
        assert result == []

    # Error path
    def test_method_raises_error_when_invalid_input(self):
        """Should raise ValueError when [condition]."""
        with pytest.raises(ValueError, match="expected message"):
            target_method(invalid_input)

Step 5: Mocking Strategy

Mock patterns by dependency type:

DependencyMock ApproachExample
DatabaseMock repository/ORM layerjest.spyOn(db, 'query').mockResolvedValue(rows)
HTTP APIMock HTTP client or use MSW/nocknock('https://api.example.com').get('/users').reply(200, data)
File systemMock fs module or use memfsjest.mock('fs/promises')
Time/DateFake timersjest.useFakeTimers(); jest.setSystemTime(new Date('2026-01-15'))
RandomSeed or mockjest.spyOn(Math, 'random').mockReturnValue(0.5)
EnvironmentSet/restore env varsprocess.env.NODE_ENV = 'test' in beforeEach
Third-party SDKMock the clientjest.mock('stripe', () => mockStripeClient)

Mock verification patterns:

typescript
// Verify a dependency was called correctly
expect(mockDb.query).toHaveBeenCalledWith(
  'SELECT * FROM users WHERE id = $1',
  [userId]
);

// Verify call count
expect(mockApi.fetch).toHaveBeenCalledTimes(1);

// Verify NOT called (important for caching tests)
expect(mockDb.query).not.toHaveBeenCalled();

// Verify call order (important for transaction tests)
const order = [];
mockDb.begin.mockImplementation(() => order.push('begin'));
mockDb.commit.mockImplementation(() => order.push('commit'));
// ... run test ...
expect(order).toEqual(['begin', 'commit']);

Step 6: Integration & E2E Tests

Integration test patterns:

typescript
describe('[Feature] Integration', () => {
  // Use real database (test instance), real logic, mock external APIs only
  let testDb: TestDatabase;

  beforeAll(async () => {
    testDb = await createTestDatabase();
    await testDb.migrate();
  });

  afterAll(async () => {
    await testDb.destroy();
  });

  beforeEach(async () => {
    await testDb.truncateAll(); // Clean state per test
  });

  it('should create user and send welcome email', async () => {
    // Arrange, mock only the email service (external boundary)
    const emailSpy = jest.spyOn(emailService, 'send').mockResolvedValue(true);

    // Act: use real DB, real validation, real business logic
    const user = await userService.register({
      email: 'test@example.com',
      name: 'Test User',
    });

    // Assert, verify real DB state + external call
    const dbUser = await testDb.query('SELECT * FROM users WHERE id = $1', [user.id]);
    expect(dbUser).toBeDefined();
    expect(emailSpy).toHaveBeenCalledWith(
      expect.objectContaining({ to: 'test@example.com' })
    );
  });
});

E2E test patterns (API):

typescript
describe('POST /api/users', () => {
  it('should create a user and return 201', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'test@example.com', name: 'Test User' })
      .expect(201);

    expect(response.body).toMatchObject({
      id: expect.any(String),
      email: 'test@example.com',
    });
  });

  it('should return 400 for invalid email', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'not-an-email', name: 'Test' })
      .expect(400);

    expect(response.body.error).toContain('email');
  });

  it('should return 409 for duplicate email', async () => {
    // Create first user
    await request(app).post('/api/users').send({ email: 'dup@example.com', name: 'First' });

    // Attempt duplicate
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'dup@example.com', name: 'Second' })
      .expect(409);
  });
});

Step 7: Coverage & CI Integration

Coverage targets:

LevelTargetRationale
Critical paths (auth, payments, data writes)>90%Bugs here = revenue loss or security issues
Business logic (services, domain)>80%Core value: must work correctly
Utilities / helpers>70%Important but lower risk
UI components>60%Visual testing often complements
Overall project>75%Healthy baseline

CI configuration template (GitHub Actions):

yaml
name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npm test -- --coverage

      - name: Check coverage thresholds
        run: |
          npx jest --coverage --coverageThreshold='{
            "global": {
              "branches": 75,
              "functions": 80,
              "lines": 80,
              "statements": 80
            }
          }'

Test runner configuration:

json
// jest.config.js / vitest.config.ts
{
  "collectCoverageFrom": [
    "src/**/*.{ts,tsx}",
    "!src/**/*.d.ts",
    "!src/**/index.ts",
    "!src/**/*.stories.tsx"
  ],
  "coverageThresholds": {
    "global": {
      "branches": 75,
      "functions": 80,
      "lines": 80,
      "statements": 80
    }
  }
}

Pre-commit test check:

bash
# Only run tests related to changed files (fast feedback)
npx jest --onlyChanged
# or
npx vitest --changed

Output Format

markdown
# Test Suite: [Target Module/Function]

## Analysis
- **Target:** [What's being tested]
- **Dependencies:** [X] total ([X] mocked, [X] real)
- **Branching paths:** [X]

## Test Cases
### Unit Tests ([X] tests)
[Test code from Step 4]

### Edge Cases ([X] tests)
[Edge case tests from Step 3]

### Error Cases ([X] tests)
[Error path tests from Step 3]

### Integration Tests ([X] tests)
[Integration tests from Step 6, if requested]

## Coverage Summary
- Lines: [X]%
- Branches: [X]%
- Functions: [X]%

## CI Integration
[CI config from Step 7, if requested]

Completion

Test Writer: Complete!

Target: [Module/function name]
Tests written: [X] total
  - Unit tests: [X]
  - Edge cases: [X]
  - Error cases: [X]
  - Integration tests: [X]
Mocked dependencies: [X]
Estimated coverage: [X]%

Next steps:
1. Run the test suite: [test command]
2. Check coverage report for uncovered branches
3. Add tests to CI pipeline (config provided above)
4. Add pre-commit hook to run related tests on save
5. Review and adjust coverage thresholds quarterly

Level History

  • Lv.1: Base: Code analysis template (inputs, outputs, dependencies, branches), 3-category test case design (happy path, edge cases, error paths), test templates for JS/TS and Python, 7 mock patterns by dependency type with verification, integration and E2E test patterns, coverage targets by code criticality, CI integration with GitHub Actions. (Origin: MemStack Pro v3.2, Mar 2026)

Frequently asked questions

What does the Memstack Development Test Writer AI skill do?

Use this skill when the user says 'write tests', 'add tests', 'test coverage', 'unit tests', 'integration tests', 'component tests', 'mocking', 'edge cases', or needs to generate tests with proper mocking and edge case coverage. Do NOT use for refactoring plans or database migrations.

Why use Memstack Development Test Writer on TypingMind?

Because you install it once and use it with any model. Memstack Development Test Writer is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Memstack Development Test Writer in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/cwinvestments/memstack/tree/master/skills/development/test-writer. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Memstack Development Test Writer?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Memstack Development Test Writer?

As many as you like. As long as a model supports skills, you can use Memstack Development Test Writer with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Memstack Development Test Writer AI skill free?

Yes. It is published on GitHub by cwinvestments under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇