By the end of this lesson, you will be able to implement comprehensive testing strategies that ensure your portfolio app works reliably across different devices, platforms, and user scenarios.
💡 Portfolio Tip: A well-tested app demonstrates professional development practices and attention to quality. Testing skills are highly valued by employers and clients.
Professional developers always test their apps:
Test Type | Purpose | Portfolio Value |
---|---|---|
Unit Tests | Test individual functions | Shows code quality |
Component Tests | Test UI components | Demonstrates frontend skills |
Integration Tests | Test feature workflows | Proves system thinking |
Manual Testing | Test user experience | Shows UX awareness |
// __tests__/Button.test.tsx
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import Button from '../components/Button';
describe('Button Component', () => {
it('renders correctly', () => {
const { getByText } = render(
<Button title="Test Button" onPress={() => {}} />
);
expect(getByText('Test Button')).toBeTruthy();
});
it('calls onPress when tapped', () => {
const mockOnPress = jest.fn();
const { getByText } = render(
<Button title="Test Button" onPress={mockOnPress} />
);
fireEvent.press(getByText('Test Button'));
expect(mockOnPress).toHaveBeenCalledTimes(1);
});
it('is disabled when loading', () => {
const { getByText } = render(
<Button title="Test Button" onPress={() => {}} loading={true} />
);
const button = getByText('Test Button').parent;
expect(button).toBeDisabled();
});
});
// __tests__/api.test.ts
import { ApiService } from '../services/ApiService';
// Mock API responses
global.fetch = jest.fn();
describe('ApiService', () => {
beforeEach(() => {
(fetch as jest.Mock).mockClear();
});
it('fetches user data successfully', async () => {
const mockUser = { id: 1, name: 'Test User' };
(fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => mockUser,
});
const result = await ApiService.getUser(1);
expect(fetch).toHaveBeenCalledWith('/api/users/1');
expect(result).toEqual(mockUser);
});
it('handles API errors gracefully', async () => {
(fetch as jest.Mock).mockResolvedValueOnce({
ok: false,
status: 404,
});
await expect(ApiService.getUser(999)).rejects.toThrow('User not found');
});
});
// __tests__/LoginFlow.test.tsx
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import LoginScreen from '../screens/LoginScreen';
import AuthService from '../services/AuthService';
jest.mock('../services/AuthService');
describe('Login Flow', () => {
it('logs in user with valid credentials', async () => {
const mockLogin = AuthService.login as jest.MockedFunction<typeof AuthService.login>;
mockLogin.mockResolvedValueOnce({ success: true, token: 'abc123' });
const { getByPlaceholderText, getByText } = render(<LoginScreen />);
// Fill form
fireEvent.changeText(getByPlaceholderText('Email'), 'test@example.com');
fireEvent.changeText(getByPlaceholderText('Password'), 'password123');
// Submit
fireEvent.press(getByText('Login'));
// Wait for API call
await waitFor(() => {
expect(mockLogin).toHaveBeenCalledWith('test@example.com', 'password123');
});
});
it('shows error for invalid credentials', async () => {
const mockLogin = AuthService.login as jest.MockedFunction<typeof AuthService.login>;
mockLogin.mockRejectedValueOnce(new Error('Invalid credentials'));
const { getByPlaceholderText, getByText, findByText } = render(<LoginScreen />);
fireEvent.changeText(getByPlaceholderText('Email'), 'wrong@example.com');
fireEvent.changeText(getByPlaceholderText('Password'), 'wrongpassword');
fireEvent.press(getByText('Login'));
expect(await findByText('Invalid credentials')).toBeTruthy();
});
});
// Create a testing checklist component
const TestingChecklist = () => {
const [testResults, setTestResults] = useState({
iPhone: { startup: false, navigation: false, features: false },
Android: { startup: false, navigation: false, features: false },
tablet: { startup: false, navigation: false, features: false },
});
const testScenarios = [
'App launches within 3 seconds',
'All navigation works smoothly',
'Core features function correctly',
'Forms validate properly',
'Images load correctly',
'Offline functionality works',
'Push notifications arrive',
'App survives background/foreground cycles',
];
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>Manual Testing Checklist</Text>
{testScenarios.map((scenario, index) => (
<TestScenarioItem
key={index}
scenario={scenario}
onResult={(passed) => updateTestResult(index, passed)}
/>
))}
</ScrollView>
);
};
// Platform-specific testing utilities
class TestingUtils {
static async testPerformance(componentName: string, testFunction: () => Promise<void>) {
const startTime = performance.now();
try {
await testFunction();
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`✅ ${componentName}: ${duration.toFixed(2)}ms`);
if (duration > 1000) {
console.warn(`⚠️ ${componentName} is slow (${duration.toFixed(2)}ms)`);
}
return { success: true, duration };
} catch (error) {
console.error(`❌ ${componentName} failed:`, error);
return { success: false, error: error.message };
}
}
static testOnMultiplePlatforms(testFunction: () => void) {
console.log(`Testing on ${Platform.OS} ${Platform.Version}`);
try {
testFunction();
console.log(`✅ Test passed on ${Platform.OS}`);
} catch (error) {
console.error(`❌ Test failed on ${Platform.OS}:`, error);
}
}
static async testNetworkConditions(apiCall: () => Promise<any>) {
const conditions = ['fast', 'slow', 'offline'];
for (const condition of conditions) {
console.log(`Testing under ${condition} network conditions`);
try {
// Simulate network condition
if (condition === 'slow') {
await new Promise(resolve => setTimeout(resolve, 2000));
}
if (condition === 'offline') {
throw new Error('Network unavailable');
}
await apiCall();
console.log(`✅ ${condition} network test passed`);
} catch (error) {
console.log(`⚠️ ${condition} network test failed: ${error.message}`);
}
}
}
}
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"preset": "react-native",
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
// __tests__/setup.ts
import 'react-native-gesture-handler/jestSetup';
// Mock AsyncStorage
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
// Mock navigation
jest.mock('@react-navigation/native', () => ({
useNavigation: () => ({
navigate: jest.fn(),
goBack: jest.fn(),
}),
useRoute: () => ({
params: {},
}),
}));
// Global test utilities
global.testUtils = {
createMockUser: () => ({
id: 1,
name: 'Test User',
email: 'test@example.com',
}),
mockApiResponse: (data: any) => ({
ok: true,
json: async () => data,
}),
};
Authentication Flow
Data Management
User Interface
Performance
// Create a test report for your portfolio
const generateTestReport = () => {
const testSuites = [
{ name: 'Authentication', tests: 12, passed: 12, coverage: 95 },
{ name: 'User Interface', tests: 28, passed: 27, coverage: 88 },
{ name: 'Data Management', tests: 15, passed: 15, coverage: 92 },
{ name: 'Performance', tests: 8, passed: 8, coverage: 100 },
];
const totalTests = testSuites.reduce((sum, suite) => sum + suite.tests, 0);
const totalPassed = testSuites.reduce((sum, suite) => sum + suite.passed, 0);
const averageCoverage = testSuites.reduce((sum, suite) => sum + suite.coverage, 0) / testSuites.length;
return {
summary: {
total: totalTests,
passed: totalPassed,
failed: totalTests - totalPassed,
coverage: averageCoverage,
status: totalPassed === totalTests ? 'PASSED' : 'FAILED'
},
suites: testSuites
};
};
You learned comprehensive testing strategies including unit testing with Jest, component testing, integration testing, and manual testing approaches. These testing skills demonstrate professional development practices and ensure your portfolio app works reliably.
In the next lesson, we'll explore launch and deployment strategies to get your portfolio app ready for publication on the App Store and Google Play.