342 lines
8.0 KiB
Markdown
342 lines
8.0 KiB
Markdown
# Testing Guide for Copper Tone Technologies
|
|
|
|
This document describes the testing strategy and implementation for the Copper Tone Technologies platform.
|
|
|
|
## Testing Stack
|
|
|
|
### Frontend Testing
|
|
- **Unit Tests:** Vitest + Vue Test Utils
|
|
- **E2E Tests:** Cypress
|
|
- **Coverage:** Configured for comprehensive test coverage reporting
|
|
|
|
### Backend Testing
|
|
- **Unit Tests:** Go standard testing library (`testing` package)
|
|
- **Integration Tests:** Testing against real database with test fixtures
|
|
- **Test Database:** Separate database for testing (configured via environment)
|
|
|
|
---
|
|
|
|
## Frontend Testing
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
cd frontend
|
|
|
|
# Run unit tests
|
|
npm run test:unit
|
|
|
|
# Run unit tests in watch mode
|
|
npm run test:unit -- --watch
|
|
|
|
# Run unit tests with coverage
|
|
npm run test:unit -- --coverage
|
|
|
|
# Run E2E tests (headless)
|
|
npm run test:e2e
|
|
|
|
# Run E2E tests (interactive)
|
|
npm run test:e2e:dev
|
|
```
|
|
|
|
### Unit Test Structure
|
|
|
|
Unit tests are located in `src/**/__tests__/` directories, co-located with the code they test.
|
|
|
|
**Example: Store Test**
|
|
```typescript
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
import { useAuthStore } from '../auth'
|
|
|
|
describe('Auth Store', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('initializes with correct default state', () => {
|
|
const store = useAuthStore()
|
|
expect(store.user).toBeNull()
|
|
expect(store.token).toBeNull()
|
|
})
|
|
})
|
|
```
|
|
|
|
**Example: Component Test**
|
|
```typescript
|
|
import { describe, it, expect } from 'vitest'
|
|
import { mount } from '@vue/test-utils'
|
|
import HelloWorld from '../HelloWorld.vue'
|
|
|
|
describe('HelloWorld', () => {
|
|
it('renders properly', () => {
|
|
const wrapper = mount(HelloWorld, { props: { msg: 'Hello' } })
|
|
expect(wrapper.text()).toContain('Hello')
|
|
})
|
|
})
|
|
```
|
|
|
|
### E2E Test Structure
|
|
|
|
E2E tests are located in `cypress/e2e/` directory.
|
|
|
|
**Example: Authentication Flow Test**
|
|
```typescript
|
|
describe('Authentication Flow', () => {
|
|
it('can login with email and password', () => {
|
|
cy.intercept('POST', '**/login-email-password', {
|
|
statusCode: 200,
|
|
body: { token: 'mock-jwt-token' }
|
|
}).as('login')
|
|
|
|
cy.visit('/login')
|
|
cy.get('input[type="email"]').type('test@example.com')
|
|
cy.get('input[type="password"]').type('password123')
|
|
cy.get('button[type="submit"]').click()
|
|
|
|
cy.wait('@login')
|
|
cy.url().should('include', '/dashboard')
|
|
})
|
|
})
|
|
```
|
|
|
|
### Test Coverage Goals
|
|
|
|
- **Stores:** 100% coverage (critical business logic)
|
|
- **Components:** 80%+ coverage
|
|
- **Views:** 70%+ coverage (covered by E2E)
|
|
- **Utilities:** 100% coverage
|
|
|
|
---
|
|
|
|
## Backend Testing
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
cd backend/functions/<service-name>
|
|
|
|
# Run all tests
|
|
go test ./...
|
|
|
|
# Run tests with verbose output
|
|
go test -v ./...
|
|
|
|
# Run tests with coverage
|
|
go test -cover ./...
|
|
|
|
# Generate coverage report
|
|
go test -coverprofile=coverage.out ./...
|
|
go tool cover -html=coverage.out
|
|
```
|
|
|
|
### Test Structure
|
|
|
|
Tests are in `*_test.go` files co-located with the code they test.
|
|
|
|
**Example: Handler Test**
|
|
```go
|
|
func TestHandleRegisterEmailPassword(t *testing.T) {
|
|
// Setup test database
|
|
testDB := setupTestDB(t)
|
|
defer testDB.Close()
|
|
|
|
// Create test request
|
|
reqBody := RegisterEmailPasswordRequest{
|
|
Email: "test@example.com",
|
|
Password: "SecurePass123!",
|
|
Name: "Test User",
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
req := httptest.NewRequest("POST", "/register-email-password", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
|
|
// Execute handler
|
|
handleRegisterEmailPassword(w, req)
|
|
|
|
// Assert response
|
|
if w.Code != http.StatusCreated {
|
|
t.Errorf("Expected status 201, got %d", w.Code)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Example: Service Function Test**
|
|
```go
|
|
func TestVerifyEthereumSignature(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
address string
|
|
message string
|
|
signature string
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "valid signature",
|
|
address: "0x1234...",
|
|
message: "Test message",
|
|
signature: "0xabcd...",
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "invalid signature",
|
|
address: "0x1234...",
|
|
message: "Test message",
|
|
signature: "0xinvalid",
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := verifyEthereumSignature(tc.address, tc.message, tc.signature)
|
|
if result != tc.expected {
|
|
t.Errorf("Expected %v, got %v", tc.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### Test Coverage Goals
|
|
|
|
- **Handlers:** 90%+ coverage
|
|
- **Business Logic:** 100% coverage
|
|
- **Utilities:** 100% coverage
|
|
- **Middleware:** 95%+ coverage
|
|
|
|
---
|
|
|
|
## CI/CD Integration
|
|
|
|
### Gitea Actions Workflows
|
|
|
|
Tests are automatically run on:
|
|
- Pull requests to `develop`
|
|
- Commits to `develop`, `testing`, `main`
|
|
|
|
**Frontend Workflow** (`.gitea/workflows/frontend.yml`):
|
|
```yaml
|
|
- name: Run Unit Tests
|
|
run: |
|
|
cd frontend
|
|
npm run test:unit -- --run
|
|
|
|
- name: Run E2E Tests
|
|
run: |
|
|
cd frontend
|
|
npm run build
|
|
npm run test:e2e
|
|
```
|
|
|
|
**Backend Workflow** (`.gitea/workflows/backend.yml`):
|
|
```yaml
|
|
- name: Run Tests
|
|
run: |
|
|
cd backend/functions/auth-service
|
|
go test -v -cover ./...
|
|
cd ../work-management-service
|
|
go test -v -cover ./...
|
|
cd ../payment-service
|
|
go test -v -cover ./...
|
|
```
|
|
|
|
---
|
|
|
|
## Test Data & Fixtures
|
|
|
|
### Frontend Test Data
|
|
|
|
Mock data for frontend tests is in `src/**/__tests__/fixtures/`:
|
|
|
|
```typescript
|
|
export const mockUser = {
|
|
id: 1,
|
|
name: 'Test User',
|
|
email: 'test@example.com',
|
|
roles: ['CLIENT'],
|
|
createdAt: new Date().toISOString()
|
|
}
|
|
|
|
export const mockProjects = [
|
|
{ id: 1, name: 'Project Alpha', status: 'ACTIVE', ... },
|
|
{ id: 2, name: 'Project Beta', status: 'PLANNING', ... }
|
|
]
|
|
```
|
|
|
|
### Backend Test Data
|
|
|
|
Test fixtures in `testdata/` directories:
|
|
|
|
```
|
|
backend/functions/auth-service/
|
|
testdata/
|
|
users.json
|
|
identities.json
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### Frontend Testing
|
|
|
|
1. **Use Descriptive Test Names:** `it('redirects to login when accessing protected route while unauthenticated')`
|
|
2. **Mock External Dependencies:** Use `vi.mock()` for API calls, localStorage, etc.
|
|
3. **Test User Interactions:** Focus on user behavior, not implementation details
|
|
4. **Avoid Testing Framework Internals:** Don't test Vue Router, Pinia, etc. - test YOUR code
|
|
5. **Use Semantic Queries:** `cy.contains('button', 'Login')` instead of CSS selectors when possible
|
|
|
|
### Backend Testing
|
|
|
|
1. **Use Table-Driven Tests:** Test multiple scenarios with a single test function
|
|
2. **Test Error Paths:** Don't just test happy path - test failures, edge cases
|
|
3. **Use Subtests:** `t.Run(name, func(t *testing.T) { ... })` for organized output
|
|
4. **Clean Up Resources:** Use `defer db.Close()`, `defer cleanup()`
|
|
5. **Isolate Tests:** Each test should be independent and idempotent
|
|
|
|
---
|
|
|
|
## Coverage Reports
|
|
|
|
### Frontend Coverage
|
|
|
|
Generate HTML coverage report:
|
|
```bash
|
|
cd frontend
|
|
npm run test:unit -- --coverage
|
|
# Open coverage/index.html in browser
|
|
```
|
|
|
|
### Backend Coverage
|
|
|
|
Generate HTML coverage report:
|
|
```bash
|
|
cd backend/functions/auth-service
|
|
go test -coverprofile=coverage.out ./...
|
|
go tool cover -html=coverage.out -o coverage.html
|
|
# Open coverage.html in browser
|
|
```
|
|
|
|
---
|
|
|
|
## Continuous Improvement
|
|
|
|
- **Review Coverage Weekly:** Identify untested code paths
|
|
- **Add Tests for Bug Fixes:** Every bug fix should include a regression test
|
|
- **Update E2E Tests:** When adding new features, add corresponding E2E tests
|
|
- **Performance Testing:** Monitor test execution time, keep tests fast
|
|
- **Flaky Tests:** Fix or remove tests that intermittently fail
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
- [Vitest Documentation](https://vitest.dev/)
|
|
- [Vue Test Utils](https://test-utils.vuejs.org/)
|
|
- [Cypress Documentation](https://docs.cypress.io/)
|
|
- [Go Testing Package](https://pkg.go.dev/testing)
|
|
- [Go Table Driven Tests](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests)
|