Testing
React Native Audio API provides a comprehensive mock implementation to help you test your audio-related code without requiring actual audio hardware or platform-specific implementations.
Mock Implementation
The mock implementation provides the same API surface as the real library but with no-op or simplified implementations that are perfect for unit testing.
Importing Mocks
- Jest
- Manual Setup
import * as MockAudioAPI from 'react-native-audio-api/mock';
// Or import specific components
import { AudioContext, AudioRecorder } from 'react-native-audio-api/mock';
// In your test setup file
jest.mock('react-native-audio-api', () =>
require('react-native-audio-api/mock')
);
// Then in your tests
import { AudioContext, AudioRecorder } from 'react-native-audio-api';
Basic Usage
Audio Context Testing
import { AudioContext } from 'react-native-audio-api/mock';
describe('Audio Graph Tests', () => {
it('should create and connect audio nodes', () => {
const context = new AudioContext();
// Create nodes
const oscillator = context.createOscillator();
const gainNode = context.createGain();
// Configure properties
oscillator.frequency.value = 440; // A4 note
gainNode.gain.value = 0.5; // 50% volume
// Connect the audio graph
oscillator.connect(gainNode);
gainNode.connect(context.destination);
// Test the configuration
expect(oscillator.frequency.value).toBe(440);
expect(gainNode.gain.value).toBe(0.5);
});
it('should support context state management', async () => {
const context = new AudioContext();
expect(context.state).toBe('running');
await context.suspend();
expect(context.state).toBe('suspended');
await context.resume();
expect(context.state).toBe('running');
});
});
Audio Recording Testing
import { AudioContext, AudioRecorder, FileFormat, FileDirectory } from 'react-native-audio-api/mock';
describe('Audio Recording Tests', () => {
it('should configure and control recording', () => {
const context = new AudioContext();
const recorder = new AudioRecorder();
// Configure file output
const result = recorder.enableFileOutput({
format: FileFormat.M4A,
channelCount: 2,
directory: FileDirectory.Document,
});
expect(result.status).toBe('success');
// Set up recording chain
const oscillator = context.createOscillator();
const recorderAdapter = context.createRecorderAdapter();
oscillator.connect(recorderAdapter);
recorder.connect(recorderAdapter);
// Test recording workflow
const startResult = recorder.start();
expect(startResult.status).toBe('success');
expect(recorder.isRecording()).toBe(true);
const stopResult = recorder.stop();
expect(stopResult.status).toBe('success');
expect(recorder.isRecording()).toBe(false);
});
});
Offline Audio Processing
import { OfflineAudioContext } from 'react-native-audio-api/mock';
describe('Offline Processing Tests', () => {
it('should render offline audio', async () => {
const offlineContext = new OfflineAudioContext({
numberOfChannels: 2,
length: 44100, // 1 second at 44.1kHz
sampleRate: 44100,
});
// Create a simple tone
const oscillator = offlineContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.connect(offlineContext.destination);
// Render the audio
const renderedBuffer = await offlineContext.startRendering();
expect(renderedBuffer.numberOfChannels).toBe(2);
expect(renderedBuffer.length).toBe(44100);
expect(renderedBuffer.sampleRate).toBe(44100);
});
});
Advanced Testing Scenarios
Custom Worklet Testing
import { AudioContext, WorkletProcessingNode } from 'react-native-audio-api/mock';
describe('Worklet Tests', () => {
it('should create custom audio processing', () => {
const context = new AudioContext();
const processingCallback = jest.fn((inputData, outputData, framesToProcess) => {
// Mock audio processing logic
for (let channel = 0; channel < outputData.length; channel++) {
for (let i = 0; i < framesToProcess; i++) {
outputData[channel][i] = inputData[channel][i] * 0.5; // Simple gain
}
}
});
const workletNode = new WorkletProcessingNode(
context,
'AudioRuntime',
processingCallback
);
expect(workletNode.context).toBe(context);
});
});
Audio Streaming Testing
import { AudioContext } from 'react-native-audio-api/mock';
describe('Streaming Tests', () => {
it('should handle audio streaming', () => {
const context = new AudioContext();
const streamer = context.createStreamer({
streamPath: 'https://example.com/audio-stream',
});
expect(streamer.streamPath).toBe('https://example.com/audio-stream');
// Test streaming controls
streamer.start();
streamer.pause();
streamer.resume();
streamer.stop();
});
});
Error Handling Testing
import {
AudioRecorder,
NotSupportedError,
InvalidStateError
} from 'react-native-audio-api/mock';
describe('Error Handling Tests', () => {
it('should handle various error conditions', () => {
// Test error creation
expect(() => {
throw new NotSupportedError('Feature not supported');
}).toThrow('Feature not supported');
// Test recorder connection errors
const recorder = new AudioRecorder();
const context = new AudioContext();
const adapter = context.createRecorderAdapter();
// First connection should work
recorder.connect(adapter);
// Second connection should throw
expect(() => recorder.connect(adapter)).toThrow();
});
});
Mock Configuration
System Volume Testing
import { useSystemVolume, setMockSystemVolume, AudioManager } from 'react-native-audio-api/mock';
describe('System Integration Tests', () => {
it('should mock system audio management', () => {
// Test system sample rate
const preferredRate = AudioManager.getDevicePreferredSampleRate();
expect(preferredRate).toBe(44100);
// Test volume management
setMockSystemVolume(0.7);
const currentVolume = useSystemVolume();
expect(currentVolume).toBe(0.7);
// Test event listeners
const volumeCallback = jest.fn();
const listener = AudioManager.addSystemEventListener(
'volumeChange',
volumeCallback
);
expect(listener.remove).toBeDefined();
listener.remove();
});
});
Audio Callback Testing
import { AudioRecorder } from 'react-native-audio-api/mock';
describe('Callback Tests', () => {
it('should handle audio data callbacks', () => {
const recorder = new AudioRecorder();
const audioDataCallback = jest.fn();
const result = recorder.onAudioReady(
{
sampleRate: 44100,
bufferLength: 1024,
channelCount: 2,
},
audioDataCallback
);
expect(result.status).toBe('success');
// Test callback cleanup
recorder.clearOnAudioReady();
expect(() => recorder.clearOnAudioReady()).not.toThrow();
});
});
Type Safety
The mock implementation provides full TypeScript support with the same types as the real library:
import type { AudioContext, AudioParam, GainNode } from 'react-native-audio-api/mock';
// All types are available and identical to the real implementation
function processAudioNode(node: GainNode): void {
node.gain.value = 0.5;
}
Testing Best Practices
- Isolate Audio Logic: Test audio processing logic separately from UI components
- Mock External Dependencies: Use mocks for file system, network, and platform-specific operations
- Test Error Scenarios: Verify your code handles various error conditions gracefully
- Validate Audio Graph Structure: Ensure nodes are connected correctly
- Test Async Operations: Use proper async/await patterns for operations like rendering
Example Test Suite
import {
AudioContext,
AudioRecorder,
FileFormat,
decodeAudioData
} from 'react-native-audio-api/mock';
describe('Audio Application Tests', () => {
let context: AudioContext;
beforeEach(() => {
context = new AudioContext();
});
afterEach(() => {
// Clean up if needed
context.close();
});
describe('Audio Graph', () => {
it('should create complex audio processing chain', () => {
const oscillator = context.createOscillator();
const filter = context.createBiquadFilter();
const delay = context.createDelay();
const gainNode = context.createGain();
// Configure effects chain
filter.type = 'lowpass';
filter.frequency.value = 2000;
delay.delayTime.value = 0.3;
gainNode.gain.value = 0.8;
// Connect the chain
oscillator.connect(filter);
filter.connect(delay);
delay.connect(gainNode);
gainNode.connect(context.destination);
// Verify configuration
expect(filter.type).toBe('lowpass');
expect(delay.delayTime.value).toBe(0.3);
expect(gainNode.gain.value).toBe(0.8);
});
});
describe('File Operations', () => {
it('should handle audio file processing', async () => {
const mockAudioData = new ArrayBuffer(1024);
// Test audio decoding
const decodedBuffer = await decodeAudioData(mockAudioData);
expect(decodedBuffer.numberOfChannels).toBe(2);
expect(decodedBuffer.sampleRate).toBe(44100);
});
});
});
The mock implementation provides a complete testing environment that allows you to thoroughly test your audio applications without requiring real audio hardware or complex setup.