# Cross-Language Test Strategy Adaptation **Version**: 2.0 **Source**: Bootstrap-002 Test Strategy Development **Last Updated**: 2025-10-18 This document provides guidance for adapting test patterns and methodology to different programming languages and frameworks. --- ## Transferability Overview ### Universal Concepts (100% Transferable) The following concepts apply to ALL languages: 1. **Coverage-Driven Workflow**: Analyze → Prioritize → Test → Verify 2. **Priority Matrix**: P1 (error handling) → P4 (infrastructure) 3. **Pattern-Based Testing**: Structured approaches to common scenarios 4. **Table-Driven Approach**: Multiple scenarios with shared logic 5. **Error Path Testing**: Systematic edge case coverage 6. **Dependency Injection**: Mock external dependencies 7. **Quality Standards**: Test structure and best practices 8. **TDD Cycle**: Red-Green-Refactor ### Language-Specific Elements (Require Adaptation) 1. **Syntax and Imports**: Language-specific 2. **Testing Framework APIs**: Different per ecosystem 3. **Coverage Tool Commands**: Language-specific tools 4. **Mock Implementation**: Different mocking libraries 5. **Build/Run Commands**: Different toolchains --- ## Go → Python Adaptation ### Transferability: 80-90% ### Testing Framework Mapping | Go Concept | Python Equivalent | |------------|------------------| | `testing` package | `unittest` or `pytest` | | `t.Run()` subtests | `pytest` parametrize or `unittest` subtests | | `t.Helper()` | `pytest` fixtures | | `t.Cleanup()` | `pytest` fixtures with yield or `unittest` tearDown | | Table-driven tests | `@pytest.mark.parametrize` | ### Pattern Adaptations #### Pattern 1: Unit Test **Go**: ```go func TestFunction(t *testing.T) { result := Function(input) if result != expected { t.Errorf("got %v, want %v", result, expected) } } ``` **Python (pytest)**: ```python def test_function(): result = function(input) assert result == expected, f"got {result}, want {expected}" ``` **Python (unittest)**: ```python class TestFunction(unittest.TestCase): def test_function(self): result = function(input) self.assertEqual(result, expected) ``` #### Pattern 2: Table-Driven Test **Go**: ```go func TestFunction(t *testing.T) { tests := []struct { name string input int expected int }{ {"case1", 1, 2}, {"case2", 2, 4}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Function(tt.input) if result != tt.expected { t.Errorf("got %v, want %v", result, tt.expected) } }) } } ``` **Python (pytest)**: ```python @pytest.mark.parametrize("input,expected", [ (1, 2), (2, 4), ]) def test_function(input, expected): result = function(input) assert result == expected ``` **Python (unittest)**: ```python class TestFunction(unittest.TestCase): def test_cases(self): cases = [ ("case1", 1, 2), ("case2", 2, 4), ] for name, input, expected in cases: with self.subTest(name=name): result = function(input) self.assertEqual(result, expected) ``` #### Pattern 6: Dependency Injection (Mocking) **Go**: ```go type Executor interface { Execute(args Args) (Result, error) } type MockExecutor struct { Results map[string]Result } func (m *MockExecutor) Execute(args Args) (Result, error) { return m.Results[args.Key], nil } ``` **Python (unittest.mock)**: ```python from unittest.mock import Mock, MagicMock def test_process(): mock_executor = Mock() mock_executor.execute.return_value = expected_result result = process_data(mock_executor) assert result == expected mock_executor.execute.assert_called_once() ``` **Python (pytest-mock)**: ```python def test_process(mocker): mock_executor = mocker.Mock() mock_executor.execute.return_value = expected_result result = process_data(mock_executor) assert result == expected ``` ### Coverage Tools **Go**: ```bash go test -coverprofile=coverage.out ./... go tool cover -func=coverage.out go tool cover -html=coverage.out ``` **Python (pytest-cov)**: ```bash pytest --cov=package --cov-report=term pytest --cov=package --cov-report=html pytest --cov=package --cov-report=term-missing ``` **Python (coverage.py)**: ```bash coverage run -m pytest coverage report coverage html ``` --- ## Go → JavaScript/TypeScript Adaptation ### Transferability: 75-85% ### Testing Framework Mapping | Go Concept | JavaScript/TypeScript Equivalent | |------------|--------------------------------| | `testing` package | Jest, Mocha, Vitest | | `t.Run()` subtests | `describe()` / `it()` blocks | | Table-driven tests | `test.each()` (Jest) | | Mocking | Jest mocks, Sinon | | Coverage | Jest built-in, nyc/istanbul | ### Pattern Adaptations #### Pattern 1: Unit Test **Go**: ```go func TestFunction(t *testing.T) { result := Function(input) if result != expected { t.Errorf("got %v, want %v", result, expected) } } ``` **JavaScript (Jest)**: ```javascript test('function returns expected result', () => { const result = functionUnderTest(input); expect(result).toBe(expected); }); ``` **TypeScript (Jest)**: ```typescript describe('functionUnderTest', () => { it('returns expected result', () => { const result = functionUnderTest(input); expect(result).toBe(expected); }); }); ``` #### Pattern 2: Table-Driven Test **Go**: ```go func TestFunction(t *testing.T) { tests := []struct { name string input int expected int }{ {"case1", 1, 2}, {"case2", 2, 4}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Function(tt.input) if result != tt.expected { t.Errorf("got %v, want %v", result, tt.expected) } }) } } ``` **JavaScript/TypeScript (Jest)**: ```typescript describe('functionUnderTest', () => { test.each([ ['case1', 1, 2], ['case2', 2, 4], ])('%s: input %i should return %i', (name, input, expected) => { const result = functionUnderTest(input); expect(result).toBe(expected); }); }); ``` **Alternative with object syntax**: ```typescript describe('functionUnderTest', () => { test.each([ { name: 'case1', input: 1, expected: 2 }, { name: 'case2', input: 2, expected: 4 }, ])('$name', ({ input, expected }) => { const result = functionUnderTest(input); expect(result).toBe(expected); }); }); ``` #### Pattern 6: Dependency Injection (Mocking) **Go**: ```go type MockExecutor struct { Results map[string]Result } ``` **JavaScript (Jest)**: ```javascript const mockExecutor = { execute: jest.fn((args) => { return results[args.key]; }) }; test('processData uses executor', () => { const result = processData(mockExecutor, testData); expect(result).toBe(expected); expect(mockExecutor.execute).toHaveBeenCalledWith(testData); }); ``` **TypeScript (Jest)**: ```typescript const mockExecutor: Executor = { execute: jest.fn((args: Args): Result => { return results[args.key]; }) }; ``` ### Coverage Tools **Jest (built-in)**: ```bash jest --coverage jest --coverage --coverageReporters=html jest --coverage --coverageReporters=text-summary ``` **nyc (for Mocha)**: ```bash nyc mocha nyc report --reporter=html nyc report --reporter=text-summary ``` --- ## Go → Rust Adaptation ### Transferability: 70-80% ### Testing Framework Mapping | Go Concept | Rust Equivalent | |------------|----------------| | `testing` package | Built-in `#[test]` | | `t.Run()` subtests | `#[test]` functions | | Table-driven tests | Loop or macro | | Error handling | `Result` assertions | | Mocking | `mockall` crate | ### Pattern Adaptations #### Pattern 1: Unit Test **Go**: ```go func TestFunction(t *testing.T) { result := Function(input) if result != expected { t.Errorf("got %v, want %v", result, expected) } } ``` **Rust**: ```rust #[test] fn test_function() { let result = function(input); assert_eq!(result, expected); } ``` #### Pattern 2: Table-Driven Test **Go**: ```go func TestFunction(t *testing.T) { tests := []struct { name string input int expected int }{ {"case1", 1, 2}, {"case2", 2, 4}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Function(tt.input) if result != tt.expected { t.Errorf("got %v, want %v", result, tt.expected) } }) } } ``` **Rust**: ```rust #[test] fn test_function() { let tests = vec![ ("case1", 1, 2), ("case2", 2, 4), ]; for (name, input, expected) in tests { let result = function(input); assert_eq!(result, expected, "test case: {}", name); } } ``` **Rust (using rstest crate)**: ```rust use rstest::rstest; #[rstest] #[case(1, 2)] #[case(2, 4)] fn test_function(#[case] input: i32, #[case] expected: i32) { let result = function(input); assert_eq!(result, expected); } ``` #### Pattern 4: Error Path Testing **Go**: ```go func TestFunction_Error(t *testing.T) { _, err := Function(invalidInput) if err == nil { t.Error("expected error, got nil") } } ``` **Rust**: ```rust #[test] fn test_function_error() { let result = function(invalid_input); assert!(result.is_err(), "expected error"); } #[test] #[should_panic(expected = "invalid input")] fn test_function_panic() { function_that_panics(invalid_input); } ``` ### Coverage Tools **tarpaulin**: ```bash cargo tarpaulin --out Html cargo tarpaulin --out Lcov ``` **llvm-cov (nightly)**: ```bash cargo +nightly llvm-cov --html cargo +nightly llvm-cov --text ``` --- ## Adaptation Checklist When adapting test methodology to a new language: ### Phase 1: Map Core Concepts - [ ] Identify language testing framework (unittest, pytest, Jest, etc.) - [ ] Map test structure (functions vs classes vs methods) - [ ] Map assertion style (if/error vs assert vs expect) - [ ] Map test organization (subtests, parametrize, describe/it) - [ ] Map mocking approach (interfaces vs dependency injection vs mocks) ### Phase 2: Adapt Patterns - [ ] Translate Pattern 1 (Unit Test) to target language - [ ] Translate Pattern 2 (Table-Driven) to target language - [ ] Translate Pattern 4 (Error Path) to target language - [ ] Identify language-specific patterns (e.g., decorator tests in Python) - [ ] Document language-specific gotchas ### Phase 3: Adapt Tools - [ ] Identify coverage tool (coverage.py, Jest, tarpaulin, etc.) - [ ] Create coverage gap analyzer script for target language - [ ] Create test generator script for target language - [ ] Adapt automation workflow to target toolchain ### Phase 4: Adapt Workflow - [ ] Update coverage generation commands - [ ] Update test execution commands - [ ] Update IDE/editor integration - [ ] Update CI/CD pipeline - [ ] Document language-specific workflow ### Phase 5: Validate - [ ] Apply methodology to sample project - [ ] Measure effectiveness (time per test, coverage increase) - [ ] Document lessons learned - [ ] Refine patterns based on feedback --- ## Language-Specific Considerations ### Python **Strengths**: - `pytest` parametrize is excellent for table-driven tests - Fixtures provide powerful setup/teardown - `unittest.mock` is very flexible **Challenges**: - Dynamic typing can hide errors caught at compile time in Go - Coverage tools sometimes struggle with decorators - Import-time code execution complicates testing **Tips**: - Use type hints to catch errors early - Use `pytest-cov` for coverage - Use `pytest-mock` for simpler mocking - Test module imports separately ### JavaScript/TypeScript **Strengths**: - Jest has excellent built-in mocking - `test.each` is natural for table-driven tests - TypeScript adds compile-time type safety **Challenges**: - Async/Promise handling adds complexity - Module mocking can be tricky - Coverage of TypeScript types vs runtime code **Tips**: - Use TypeScript for better IDE support and type safety - Use Jest's `async/await` test support - Use `ts-jest` for TypeScript testing - Mock at module boundaries, not implementation details ### Rust **Strengths**: - Built-in testing framework is simple and fast - Compile-time guarantees reduce need for some tests - `Result` makes error testing explicit **Challenges**: - Less mature test tooling ecosystem - Mocking requires more setup (mockall crate) - Lifetime and ownership can complicate test data **Tips**: - Use `rstest` for parametrized tests - Use `mockall` for mocking traits - Use integration tests (`tests/` directory) for public API - Use unit tests for internal logic --- ## Effectiveness Across Languages ### Expected Methodology Transfer | Language | Pattern Transfer | Tool Adaptation | Overall Transfer | |----------|-----------------|----------------|-----------------| | **Python** | 95% | 80% | 80-90% | | **JavaScript/TypeScript** | 90% | 75% | 75-85% | | **Rust** | 85% | 70% | 70-80% | | **Java** | 90% | 80% | 80-85% | | **C#** | 90% | 85% | 85-90% | | **Ruby** | 85% | 75% | 75-80% | ### Time to Adapt | Activity | Estimated Time | |----------|---------------| | Map core concepts | 2-3 hours | | Adapt patterns | 3-4 hours | | Create automation tools | 4-6 hours | | Validate on sample project | 2-3 hours | | Document adaptations | 1-2 hours | | **Total** | **12-18 hours** | --- **Source**: Bootstrap-002 Test Strategy Development **Framework**: BAIME (Bootstrapped AI Methodology Engineering) **Status**: Production-ready, validated through 4 iterations