Testing Guide
Comprehensive testing documentation for the Yappa Knowledge Hub POC.
Overview
The project uses a multi-layered testing approach:
- Unit Tests: Test individual services and utilities in isolation
- Integration Tests: Test Slack handlers with mocked dependencies
- API Tests: Test Symfony REST API endpoints
- E2E Tests: Test complete workflows across the system
Test Stack
Node.js / Slack Bot
- Framework: Jest 29.7.0
- HTTP Mocking: Nock 13.5.0
- Test Runner: Node with ES modules support
Symfony / Backend
- Framework: PHPUnit 9.5
- Test Type: WebTestCase for API testing
- Database: SQLite in-memory for tests
Directory Structure
tests/
unit/ # Unit tests for services
services/
knowledge.test.js
categories.test.js
urlScraper.test.js
integration/ # Integration tests for handlers
handlers/
events.test.js
interactions.test.js
commands.test.js
api/ # API endpoint tests
knowledge.test.js
categories.test.js
e2e/ # End-to-end workflow tests
workflows.test.js
fixtures/ # Mock data and test fixtures
mockData.js
backend/tests/
bootstrap.php # PHPUnit bootstrap
Entity/ # Entity unit tests
KnowledgeTest.php
CategoryTest.php
Controller/ # API controller tests
KnowledgeControllerTest.php
CategoryControllerTest.phpRunning Tests
Node.js Tests
Run all tests:
npm testRun specific test suites:
npm run test:unit # Unit tests only
npm run test:integration # Integration tests only
npm run test:api # API tests only
npm run test:e2e # E2E tests onlyWatch mode for development:
npm run test:watchGenerate coverage report:
npm run test:coverageSymfony Tests
Run all PHPUnit tests:
cd backend
./vendor/bin/phpunitRun specific test suites:
./vendor/bin/phpunit tests/Entity
./vendor/bin/phpunit tests/ControllerRun with coverage:
./vendor/bin/phpunit --coverage-html coverageTest Categories
1. Unit Tests
Test individual services in isolation with mocked dependencies.
Example: Knowledge Service
describe('Knowledge Service', () => {
it('should add knowledge with complete data', async () => {
const mockResponse = { data: mockKnowledge };
axios.post.mockResolvedValue(mockResponse);
const result = await knowledgeService.addKnowledge(data, 'U12345');
expect(axios.post).toHaveBeenCalledWith(
'http://localhost:8000/api/knowledge',
expect.objectContaining({ title: 'Test Knowledge' })
);
});
});Coverage:
/home/ubuntu/yappa-knowledge-hub/tests/unit/services/knowledge.test.js/home/ubuntu/yappa-knowledge-hub/tests/unit/services/categories.test.js/home/ubuntu/yappa-knowledge-hub/tests/unit/services/urlScraper.test.js
2. Integration Tests
Test Slack handlers with mocked Slack client and service dependencies.
Example: Events Handler
describe('Events Handler', () => {
it('should detect and scrape URLs in messages', async () => {
urlScraperService.scrapeUrlMetadata.mockResolvedValue(mockUrlMetadata);
await handleMessageEvent({ event, client: mockClient });
expect(urlScraperService.scrapeUrlMetadata).toHaveBeenCalled();
});
});Coverage:
/home/ubuntu/yappa-knowledge-hub/tests/integration/handlers/events.test.js/home/ubuntu/yappa-knowledge-hub/tests/integration/handlers/interactions.test.js/home/ubuntu/yappa-knowledge-hub/tests/integration/handlers/commands.test.js
3. API Tests
Test Symfony REST API endpoints with real HTTP requests.
Example: Knowledge API
describe('Knowledge API Endpoints', () => {
it('should create new knowledge item', async () => {
const response = await axios.post(`${API_BASE_URL}/knowledge`, payload);
expect(response.status).toBe(201);
expect(response.data.title).toBe('API Test Knowledge');
});
});Coverage:
/home/ubuntu/yappa-knowledge-hub/tests/api/knowledge.test.js/home/ubuntu/yappa-knowledge-hub/tests/api/categories.test.js
4. E2E Tests
Test complete workflows across the entire system.
Example: Complete Workflow
describe('Complete Knowledge Management Workflow', () => {
it('should create category, add knowledge, search, update, and delete', async () => {
// Create category
const catResponse = await axios.post(`${API_BASE_URL}/categories`, category);
// Add knowledge
const knResponse = await axios.post(`${API_BASE_URL}/knowledge`, knowledge);
// Search
const searchResponse = await axios.get(`${API_BASE_URL}/knowledge`, {
params: { search: 'test' }
});
// Cleanup
await axios.delete(`${API_BASE_URL}/knowledge/${knowledgeId}`);
await axios.delete(`${API_BASE_URL}/categories/${categoryId}`);
});
});Coverage:
/home/ubuntu/yappa-knowledge-hub/tests/e2e/workflows.test.js
5. PHPUnit Tests
Test Symfony entities and controllers.
Example: Entity Test
class KnowledgeTest extends TestCase
{
public function testKnowledgeCreation(): void
{
$knowledge = new Knowledge();
$knowledge->setTitle('Test Knowledge');
$this->assertEquals('Test Knowledge', $knowledge->getTitle());
}
}Example: Controller Test
class KnowledgeControllerTest extends WebTestCase
{
public function testCreateKnowledge(): void
{
$this->client->request('POST', '/api/knowledge', [], [], [
'CONTENT_TYPE' => 'application/json',
], json_encode(['title' => 'Test']));
$this->assertResponseStatusCodeSame(201);
}
}Coverage:
https://github.com/undead2146/KnowledgeHub/blob/main/backend/tests/Entity/KnowledgeTest.phphttps://github.com/undead2146/KnowledgeHub/blob/main/backend/tests/Entity/CategoryTest.phphttps://github.com/undead2146/KnowledgeHub/blob/main/backend/tests/Controller/KnowledgeControllerTest.phphttps://github.com/undead2146/KnowledgeHub/blob/main/backend/tests/Controller/CategoryControllerTest.php
Mock Data
All tests use centralized mock data from /home/ubuntu/yappa-knowledge-hub/tests/fixtures/mockData.js:
export const mockKnowledge = {
id: 1,
title: 'Test Knowledge Item',
content: 'Test content',
tags: ['test', 'documentation'],
category: 'general',
// ...
};
export const mockCategory = {
id: 'cat-1',
name: 'General',
icon: ':file_folder:',
// ...
};Test Configuration
Jest Configuration (package.json)
{
"jest": {
"testEnvironment": "node",
"transform": {},
"extensionsToTreatAsEsm": [".js"],
"testMatch": ["**/tests/**/*.test.js"],
"collectCoverageFrom": [
"src/**/*.js",
"!src/prototypes/**",
"!src/app.js"
]
}
}PHPUnit Configuration (phpunit.xml.dist)
<phpunit bootstrap="tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>Best Practices
1. Test Isolation
- Each test should be independent
- Use
beforeEachto reset mocks and state - Clean up test data in E2E tests
2. Mocking
- Mock external dependencies (axios, Slack client)
- Use real implementations for unit under test
- Keep mocks simple and focused
3. Assertions
- Test both success and error cases
- Verify function calls with correct parameters
- Check response structure and data
4. Coverage Goals
- Aim for 80%+ code coverage
- Focus on critical business logic
- Don't test framework code
5. Test Naming
- Use descriptive test names
- Follow pattern: "should [expected behavior] when [condition]"
- Group related tests with
describeblocks
CI/CD Integration
GitHub Actions Example
name: Tests
on: [push, pull_request]
jobs:
node-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm install
- run: npm test
- run: npm run test:coverage
php-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: cd backend && composer install
- run: cd backend && ./vendor/bin/phpunitTroubleshooting
Common Issues
Jest ES Module Errors
# Solution: Use NODE_OPTIONS flag
NODE_OPTIONS=--experimental-vm-modules jestAxios Mock Not Working
// Ensure mock is imported before the module under test
jest.mock('axios');
import * as service from '../services/knowledge.js';PHPUnit Database Errors
# Reset test database
cd backend
php bin/console doctrine:database:drop --force --env=test
php bin/console doctrine:database:create --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=testAPI Tests Failing
# Ensure Symfony server is running
cd backend
symfony server:start -dWriting New Tests
Adding a Unit Test
- Create test file in appropriate directory
- Import dependencies and mock data
- Mock external dependencies
- Write test cases with clear descriptions
- Run tests to verify
Adding an Integration Test
- Create test file in
tests/integration/ - Mock Slack client and services
- Test handler functions with various inputs
- Verify correct function calls and responses
Adding an API Test
- Create test file in
tests/api/ - Use axios to make real HTTP requests
- Test CRUD operations
- Clean up test data
Adding a PHPUnit Test
- Create test file in
backend/tests/ - Extend appropriate base class (TestCase or WebTestCase)
- Write test methods with
testprefix - Use PHPUnit assertions
Coverage Reports
View coverage reports:
Node.js:
npm run test:coverage
open coverage/index.htmlSymfony:
cd backend
./vendor/bin/phpunit --coverage-html coverage
open coverage/index.htmlContinuous Testing
For active development, use watch mode:
# Terminal 1: Node.js tests
npm run test:watch
# Terminal 2: Symfony server
cd backend && symfony server:start
# Terminal 3: Run specific tests as needed
npm run test:apiResources
Summary
This comprehensive test suite provides:
- 95+ test cases across all layers
- Unit, integration, API, and E2E coverage
- Mock data and fixtures for consistency
- Clear documentation and examples
- CI/CD ready configuration
Run npm test and cd backend && ./vendor/bin/phpunit to execute the full test suite.