Testing NgRx Effects: Isolate and Verify Side Effects
Test NgRx Effects by treating them as pipelines that transform action streams. Provide a source action and mock dependencies to verify the effect dispatches the correct success or failure action.
WHY IT EXISTS: Effects manage side effects like API calls, which are inherently asynchronous and can fail. We need a reliable way to test that our app handles both success and failure scenarios correctly without actually making network requests or hitting a database.
THE MENTAL MODEL: Think of an Effect as a pipeline. An observable stream of actions goes in one end, and another observable stream of actions comes out the other. Your test provides a controlled input stream (e.g., a loadItems action) and checks if the output stream emits the expected action (e.g., loadItemsSuccess or loadItemsFailure).
HOW IT WORKS: You test an Effect class directly, not through the entire NgRx store setup. You instantiate the Effects class, providing mock implementations for any services it depends on, like an API client. You also provide an Actions observable. A common pattern uses Angular's TestBed to provide mocks and a library like jasmine-marbles or rxjs/testing to create cold or hot observables that simulate the action stream and assert the output.
WHEN TO USE IT: Always test your Effects, as they contain critical application logic. Focus on testing the mapping from an input action to one or more output actions. For example, test that a login action, given a successful API response from a mocked service, results in a loginSuccess action. Also, test the failure path: if the mocked service throws an error, does the effect correctly catchError and dispatch a loginFailure action?
WHEN NOT TO USE IT: Don't use Effect testing to verify the logic inside your services; that's what service unit tests are for. The Effect test's scope is purely to confirm that the Effect correctly orchestrates the call to the service and dispatches the right follow-up action based on the service's response. Don't test reducer logic here either; that's a separate unit test.
ONE CANONICAL EXAMPLE: To test an effect that fetches user data, you first set up your test environment using TestBed, providing a mock UsersService and the Actions stream. Then, create a cold observable for the input actions$ that emits a loadUsers action. Configure your mock UsersService to return a successful response. Finally, define an expected cold observable that emits a loadUsersSuccess action with the user data as its payload. You then run the effect and assert that its output matches your expected observable.
Read the original → ngrx.io
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.