# Testing Standards

> **Scope:** universal
> **Layer:** 0 (always load)
> **Keywords:** test, testing, unit, integration, xunit, nunit
> **Load When:** always

Test patterns and best practices for MORPH-SPEC projects

---

## Test Pyramid

```
        /\
       /  \  E2E Tests (10%)
      /____\
     /      \
    / Integration  Tests (30%)
   /________________\
  /                  \
 /   Unit Tests (60%)  \
/_______________________\
```

### Distribution

- **Unit Tests (60%)**: Fast, isolated, test single units
- **Integration Tests (30%)**: Test component interactions
- **E2E Tests (10%)**: Full user workflows

---

## Naming Conventions

### Format

```
MethodName_Scenario_ExpectedBehavior
```

### Examples

```csharp
// .NET (xUnit)
[Fact]
public void Login_ValidCredentials_ReturnsAuthToken() { }

[Fact]
public void Login_InvalidPassword_ThrowsUnauthorizedException() { }

[Theory]
[InlineData("user@example.com", "password123")]
public void Register_ValidInput_CreatesUser(string email, string password) { }
```

```typescript
// JavaScript (Jest/Vitest)
describe('UserService', () => {
  it('should return auth token for valid credentials', () => {})

  it('should throw error for invalid password', () => {})

  it.each([
    ['user@example.com', 'password123'],
    ['admin@test.com', 'admin456']
  ])('should create user for %s', (email, password) => {})
})
```

---

## Unit Testing Best Practices

### Arrange-Act-Assert (AAA)

```csharp
[Fact]
public void CalculateTotal_WithDiscount_AppliesCorrectPercentage()
{
    // Arrange
    var cart = new ShoppingCart();
    cart.AddItem(new Item { Price = 100 });
    var discountService = new DiscountService();

    // Act
    var total = discountService.CalculateTotal(cart, discountPercent: 10);

    // Assert
    Assert.Equal(90, total);
}
```

### Mocking Dependencies

```csharp
[Fact]
public async Task GetUser_ValidId_ReturnsUser()
{
    // Arrange
    var mockRepo = new Mock<IUserRepository>();
    mockRepo.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(new User { Id = 1, Name = "John" });

    var service = new UserService(mockRepo.Object);

    // Act
    var user = await service.GetUserAsync(1);

    // Assert
    Assert.NotNull(user);
    Assert.Equal("John", user.Name);
}
```

---

## Integration Testing

### Database Tests

```csharp
public class UserRepositoryTests : IClassFixture<DatabaseFixture>
{
    private readonly AppDbContext _context;

    public UserRepositoryTests(DatabaseFixture fixture)
    {
        _context = fixture.CreateContext();
    }

    [Fact]
    public async Task CreateUser_ValidData_SavesToDatabase()
    {
        // Arrange
        var repo = new UserRepository(_context);
        var user = new User { Email = "test@example.com" };

        // Act
        await repo.CreateAsync(user);
        await _context.SaveChangesAsync();

        // Assert
        var saved = await _context.Users.FirstOrDefaultAsync(u => u.Email == "test@example.com");
        Assert.NotNull(saved);
    }
}
```

### API Tests

```csharp
public class AuthControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public AuthControllerTests(WebApplicationFactory<Program> factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Login_ValidCredentials_Returns200AndToken()
    {
        // Arrange
        var request = new LoginRequest { Email = "user@test.com", Password = "password" };

        // Act
        var response = await _client.PostAsJsonAsync("/api/auth/login", request);

        // Assert
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<AuthResponse>();
        Assert.NotNull(result?.Token);
    }
}
```

---

## E2E Testing

### Playwright Example

```typescript
import { test, expect } from '@playwright/test';

test('user can login and access dashboard', async ({ page }) => {
  // Navigate to login
  await page.goto('/login');

  // Fill credentials
  await page.fill('input[name="email"]', 'user@example.com');
  await page.fill('input[name="password"]', 'password123');

  // Submit
  await page.click('button[type="submit"]');

  // Verify redirect to dashboard
  await expect(page).toHaveURL('/dashboard');
  await expect(page.locator('h1')).toContainText('Dashboard');
});
```

---

## Test Coverage

### Targets

- **Statements**: ≥ 80%
- **Branches**: ≥ 70%
- **Functions**: ≥ 80%
- **Lines**: ≥ 80%

### Running Coverage

```bash
# .NET
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

# JavaScript
npm run test:coverage
```

---

## Best Practices

### DO

✅ Write tests before or with code (TDD)
✅ Test edge cases (null, empty, invalid input)
✅ Use descriptive test names
✅ Keep tests fast (< 1s per unit test)
✅ Mock external dependencies
✅ Test one thing per test

### DON'T

❌ Test implementation details (test behavior)
❌ Rely on test execution order
❌ Use real databases in unit tests
❌ Hardcode magic values (use constants)
❌ Skip tests (fix or remove)
❌ Write tests that depend on external services

---

## Test Fixtures and Cleanup

### xUnit

```csharp
public class DatabaseFixture : IDisposable
{
    public AppDbContext CreateContext()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}")
            .Options;

        return new AppDbContext(options);
    }

    public void Dispose()
    {
        // Cleanup
    }
}
```

### Jest

```typescript
beforeEach(() => {
  // Setup before each test
});

afterEach(() => {
  // Cleanup after each test
});

beforeAll(() => {
  // Setup once before all tests
});

afterAll(() => {
  // Cleanup once after all tests
});
```

---

*MORPH-SPEC by Polymorphism Tech*
