node.jsangularmockingjasminemsw

Testing Angular Services with MSW causes timeout


EDIT: I should mention, that I only have problems during testing. When I run ng serve and use msw to serve the data everything works correctly.

I stumbled upon mswjs recently and wanted to use the mock service workers to test my frontend services without waiting on the backend team and avoid having to write mock-service classes. I setup everything according to the examples provided in the documentation. At first I got the message that stating spec 'UserService should get list of users' has no expectations.

I researched this and added a done() function call at the end of my subscribe callback. After doing that, I get the following error:

Error: Timeout - Async function did not complete within 3000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) in node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 7609)

I already tried increasing the default_timout in Karma but even setting it to 30.000 did not change the result.

I also tried working around by using waitForAsync without any success. This way I get no error and the test succeeds but only because it still finds no expectations within the spec.

Most example I found online do not deal with mock service workers and instead resort to using mock-services and fakeasync which does not help in my case.

This is how my code looks like:

My Angular Service:

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private url = 'http://localhost:3000/api/users';
  constructor(private http: HttpClient) { }

  getUser(id: string): Observable<User> {
    return this.http.get<User>(`${this.url}/${id}`);
  }

  listUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.url);
  }
}

My Test Code:

describe('UserService', () => {
  let service: UserService;
  beforeAll(() => {
    worker.start();
  });
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
    });
    service = TestBed.inject(UserService);
  });

  afterAll(() => {
    worker.stop();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should get list of users', (done) => {
    service.listUsers().subscribe((data) => {
      expect(data.length).toBe(5);
      done();
    });
  })
});

The Worker setup:

const handlers = [
    rest.get('http://localhost:3000/api/users', (req, res, ctx) => {
        return res(
            ctx.status(200),
            ctx.json(users))
    })
]
export const worker = setupWorker(...handlers)

Solution

  • I managed to solve my own problem by using firstValueFrom and waitForAsync. I changed the code in my tests to the following:

    it('should get list of users', waitForAsync(async () => {
       const source$ = service.listUsers();
       const result = await firstValueFrom(source$);
       expect(result.length).toEqual(5);
    }));