Unit Test Your ngrx/effects in Angular
This post assumes that you at least have some working knowledge of Angular, Jasmine, and @ngrx/effects.
If you have no prior knowledge on the subject, you can read my previous articles and then come back.
Let’s start with a simple example that we can test. We’ll create a basic redux flow that will fetch blog posts from the server.
Build the Posts Reducer —
Build the Posts Action Creators —
Now let’s create the service that will be responsible for fetching the posts from the server.
Build the Posts Service —
Build the Posts Effects —
We need to listen to GET_POSTS
action, then call our service and based on the response status to do the following thing:
- If the response is successful, dispatch
GET_POSTS_SUCCESS
action with the result. - If the request fails, dispatch
GET_POSTS_FAIL
action with the error.
These are the two cases we need to test
Let’s create the test and explain each part.
TestBed
creates an Angular testing module — an @NgModule
class—that you configure with the configureTestingModule
method to produce the module environment for the class you want to test.
The configureTestingModule
method takes an @NgModule
-like metadata object. The metadata object can have most of the properties of a normal Angular module.
We need to import the EffectsTestingModule
from @ngrx/effects
. We also need two providers:
- The original
PostsEffects
. - A mock of our
PostsService.get()
method.
One of the reasons we want to mock the service get()
method is because we do not want to make an HTTP request. In addition to that, In this case, we do not care about the method implementation; we care about the result.
Let’s stop for a second to explain this piece.
jasmine.createSpyObj
is used to create a mock that will spy on one or more methods. It returns an object that has a property for each string that is a spy.
What we are saying here is, Hey Angular when you need the PostsService
, please provide this mock.
Next, we can get a reference to the providers
with the help of the TestBed.get(provider)
method.
EffectsRunner
is provided by the EffectsTestingModule
.
Now we can write our first case.
postsService.get()
is a jasmine spy, therefore we can control the return value. In this case, we are returning mock data.
Next, we are calling the queue()
method that her job is to queue up actions.
The last thing we need to do is to subscribe to the postEffects.get$
observable and check if the result is the same as our mock data.
Now, let’s test the second part.
The same process as before, smooth and clean.
Let’s finish with a more advanced case, add debounceTime
to our effect.
debounceTime
discard emitted values that take less than the specified time between output.
We need to control time
Luckily, Angular provides a function that does just that — fakeAsync
.
We can “control” time with the tick()
function. Calling tick()
simulates the passage of time until all pending asynchronous activities finish.
When 400 milliseconds have not yet passed, the result should be null
. Otherwise, the result should be what returns from the getPostsFail('error')
function.
The final code.
Follow me on Medium or Twitter to read more about Angular, Vue and JS!