This content originally appeared on DEV Community and was authored by Gabriel Luis Freitas
The Problem
When writing tests for applications that interact with external services, we want to ensure that:
- No real external calls are made during testing
- We have predictable test data
- Tests run quickly without external dependencies
- We get clear error messages when something is misconfigured
The Solution
To fix these problems, we can follow this simple pattern:
- A utility function to prevent real service calls
- A testing provider that mocks the service
- Implementation in the actual service
1. The Error Prevention Utility
export const throwErrorIfTesting = (serviceName: string, testingProviderName: string): void => {
if (window.location.port === '9876') { // Karma's default test port
throw new Error(
`${serviceName} is not available in test mode, add ${testingProviderName} to the providers to fix the error`
);
}
};
This utility function detects when code is running in a test environment (by checking Karma’s default port) and throws a helpful error message if the real service is accidentally used.
2. The Testing Provider
export const provideApiTesting = () => ({
provide: ApiService,
useValue: {
getElements: jasmine.createSpy('getElements').and.returnValue(Promise.resolve([]))
}
});
This provider:
- Creates a mock version of the service
- Uses Jasmine spies for the method that calls the API
- Returns predictable test data
- Can be easily configured in individual tests
3. Implementation in the Service
@Injectable({
providedIn: 'root'
})
export class ApiService{
async getElements<T>(…) {
throwErrorIfTesting('ApiService', 'provideApiTesting');
// Real service implementation
}
}
The real service implements the error prevention check at the start of each method that would make external calls.
How to Mock responses
In Your Tests, you can easily mock the responses of this provider just by injecting the service and changing the returnValue
of the Spy.
describe('YourComponent', () => {
let mockApiService: jasmine.SpyObj<ApiService>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [YourComponent],
providers: [
provideApiTesting() // Use the testing provider
]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(YourComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should load content', async () => {
const apiService= TestBed.inject(ApiService);
// Configure the mock response if needed
(apiService.getElementsas jasmine.Spy).and.returnValue(
Promise.resolve([/* your test data */])
);
// Run your test…
});
});
Benefits
- Fail-Fast Development: Issues are caught immediately with clear error messages
- Predictable Testing: No external dependencies or network calls
- Clear Separation: Test environment vs production code is clearly separated
- Type Safety: Testing providers maintain the same interface as the real service
- Easy to Mock: Simple to provide custom test data when needed
- Maintainable: Pattern is easy to understand and implement for new services
This pattern ensures that your tests remain reliable, fast, and maintainable while preventing accidental real service calls during testing.
This content originally appeared on DEV Community and was authored by Gabriel Luis Freitas

Gabriel Luis Freitas | Sciencx (2025-02-21T14:34:29+00:00) Preventing Real Service Calls in Tests: A Clean Approach with Angular. Retrieved from https://www.scien.cx/2025/02/21/preventing-real-service-calls-in-tests-a-clean-approach-with-angular/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.