Skip to main content

E2E Testing Quick Start Guide

Quick reference for setting up and running E2E tests for the FishingLog platform.

Prerequisites

  • Docker (for TestContainers/PostgreSQL)
  • Node.js 20+ (for frontend tests)
  • .NET 9.0 SDK (for backend tests)
  • AWS CLI (for Cognito test users)
  • Stripe CLI (for webhook testing)

Backend E2E Tests

Setup

  1. Create test project:
dotnet new xunit -n FishingLog.API.Tests -o FishingLog.API.Tests
cd FishingLog.API.Tests
dotnet add reference ../FishingLog.API/FishingLog.API.csproj
  1. Add required packages:
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Testcontainers.PostgreSql
dotnet add package Moq
dotnet add package FluentAssertions
  1. Create test infrastructure:
// TestWebApplicationFactory.cs
public class TestWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly PostgreSQLContainer _postgres = new PostgreSQLBuilder()
.WithImage("postgres:16")
.WithDatabase("fishinglog_test")
.Build();

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
_postgres.Start();
builder.UseEnvironment("Test");
builder.ConfigureServices(services =>
{
// Replace database connection
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (descriptor != null) services.Remove(descriptor);

services.AddDbContext<AppDbContext>(options =>
{
options.UseNpgsql(_postgres.GetConnectionString());
});
});
}

protected override void Dispose(bool disposing)
{
_postgres?.Dispose();
base.Dispose(disposing);
}
}

Running Tests

cd FishingLog.API.Tests
dotnet test

Example Test

public class AuthControllerTests : IClassFixture<TestWebApplicationFactory>
{
private readonly HttpClient _client;

public AuthControllerTests(TestWebApplicationFactory factory)
{
_client = factory.CreateClient();
}

[Fact]
public async Task Login_WithValidCredentials_ReturnsTokens()
{
// Arrange
var request = new { username = "test@example.com", password = "Test123!" };

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

// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<LoginResponse>();
Assert.NotNull(result.IdToken);
}
}

Web App E2E Tests (Playwright)

Setup

  1. Install Playwright:
cd web-app
npm install -D @playwright/test
npx playwright install
  1. Create playwright.config.ts:
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './e2e/tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: process.env.API_URL || 'http://localhost:5000',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
  1. Create auth helper:
// e2e/helpers/auth.ts
import { Page } from '@playwright/test';

export async function loginAsTestUser(page: Page) {
await page.goto('/login');
await page.fill('[name="username"]', process.env.TEST_USERNAME || 'test@example.com');
await page.fill('[name="password"]', process.env.TEST_PASSWORD || 'Test123!');
await page.click('button[type="submit"]');
await page.waitForURL('/dashboard');
}

Running Tests

cd web-app
npm run test:e2e # Run tests
npm run test:e2e:ui # Run with UI
npm run test:e2e:debug # Debug mode

Example Test

// e2e/tests/auth.spec.ts
import { test, expect } from '@playwright/test';
import { loginAsTestUser } from '../helpers/auth';

test.describe('Authentication', () => {
test('user can login and see dashboard', async ({ page }) => {
await loginAsTestUser(page);
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Dashboard');
});
});

Mobile App E2E Tests (Detox)

Setup

  1. Install Detox:
cd mobile-app
npm install -D detox
npm install -D jest-circus
  1. Create .detoxrc.js:
module.exports = {
testRunner: {
args: ['--maxWorkers=1'],
jest: {
setupTimeout: 120000,
},
},
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/FishingLog.app',
build: 'xcodebuild -workspace ios/FishingLog.xcworkspace -scheme FishingLog -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
},
devices: {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 14',
},
},
},
configurations: {
'ios.sim.debug': {
device: 'simulator',
app: 'ios.debug',
},
},
};
  1. Create auth helper:
// e2e/helpers/auth.ts
export async function loginAsTestUser() {
await element(by.id('username-input')).typeText('test@example.com');
await element(by.id('password-input')).typeText('Test123!');
await element(by.id('login-button')).tap();
await waitFor(element(by.id('home-screen'))).toBeVisible().withTimeout(5000);
}

Running Tests

cd mobile-app
npm run build:ios # Build iOS app
npm run test:e2e:ios # Run iOS tests

Example Test

// e2e/tests/auth.e2e.ts
import { loginAsTestUser } from '../helpers/auth';

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

it('user can login and see home screen', async () => {
await loginAsTestUser();
await expect(element(by.id('home-screen'))).toBeVisible();
});
});

Test Data Setup

Create Test Users in Cognito

# Admin test user
aws cognito-idp admin-create-user \
--user-pool-id us-east-2_TZtGx1T3X \
--username admin-test@fishinglog.test \
--user-attributes Name=email,Value=admin-test@fishinglog.test \
--temporary-password TempPass123! \
--message-action SUPPRESS

# Set permanent password
aws cognito-idp admin-set-user-password \
--user-pool-id us-east-2_TZtGx1T3X \
--username admin-test@fishinglog.test \
--password TestPassword123! \
--permanent

# Regular test user
aws cognito-idp admin-create-user \
--user-pool-id us-east-2_TZtGx1T3X \
--username user-test@fishinglog.test \
--user-attributes Name=email,Value=user-test@fishinglog.test \
--temporary-password TempPass123! \
--message-action SUPPRESS

aws cognito-idp admin-set-user-password \
--user-pool-id us-east-2_TZtGx1T3X \
--username user-test@fishinglog.test \
--password TestPassword123! \
--permanent

Environment Variables

Create .env.test files:

Backend:

ASPNETCORE_ENVIRONMENT=Test
ConnectionStrings__DefaultConnection=Host=localhost;Database=fishinglog_test;...
Stripe__SecretKey=sk_test_...
Stripe__PublishableKey=pk_test_...
AWS__Cognito__UserPoolId=us-east-2_TZtGx1T3X
AWS__Cognito__ClientId=6i1opt39o2n5h5ihq471l1ev07

Frontend:

NEXT_PUBLIC_API_URL=http://localhost:5000
NEXT_PUBLIC_COGNITO_USER_POOL_ID=us-east-2_TZtGx1T3X
NEXT_PUBLIC_COGNITO_CLIENT_ID=6i1opt39o2n5h5ihq471l1ev07
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
TEST_USERNAME=user-test@fishinglog.test
TEST_PASSWORD=TestPassword123!

Stripe Testing

Test Cards

  • Success: 4242 4242 4242 4242
  • Decline: 4000 0000 0000 0002
  • 3D Secure: 4000 0027 6000 3184

Webhook Testing

# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login
stripe login

# Forward webhooks to local server
stripe listen --forward-to http://localhost:5000/api/stripe/webhook

CI/CD Integration

GitHub Actions

See e2e-testing-strategy.md for complete CI/CD setup examples.

Running Tests Locally Before Push

# Backend
cd FishingLog.API.Tests && dotnet test

# Web App
cd web-app && npm run test:e2e

# Mobile App
cd mobile-app && npm run test:e2e:ios

Troubleshooting

Backend Tests

Issue: Database connection fails

  • Solution: Ensure Docker is running and TestContainers can start PostgreSQL

Issue: Authentication fails

  • Solution: Check Cognito test user exists and credentials are correct

Web App Tests

Issue: Tests timeout

  • Solution: Increase timeout in playwright.config.ts

Issue: Element not found

  • Solution: Use data-testid attributes for stable selectors

Mobile App Tests

Issue: Simulator not starting

  • Solution: Ensure Xcode command line tools are installed

Issue: Build fails

  • Solution: Run cd ios && pod install first

Next Steps

  1. Set up test projects using this guide
  2. Write tests for critical paths (auth, payments)
  3. Integrate with CI/CD
  4. Expand test coverage gradually

See E2E Testing Strategy for comprehensive strategy and best practices.