Testing HTTP Requests Using MockBackend

To unit test our services, we don't want to make actual HTTP requests. To accomplish this, we need to mock out our HTTP services. Angular provides us with a MockBackend class that can be configured to provide mock responses to our requests, without actually making a network request.

The configured MockBackend can then be injected into HTTP, so any calls to the service, such as http.get will return our expected data, allowing us to test our service in isolation from real network traffic.

wikisearch.spec.ts

  1. import {
  2. fakeAsync,
  3. inject,
  4. TestBed
  5. } from '@angular/core/testing';
  6. import {
  7. HttpModule,
  8. XHRBackend,
  9. ResponseOptions,
  10. Response,
  11. RequestMethod
  12. } from '@angular/http';
  13. import {
  14. MockBackend,
  15. MockConnection
  16. } from '@angular/http/testing/mock_backend';
  17. import {SearchWiki} from './wikisearch.service';
  18. const mockResponse = {
  19. "batchcomplete": "",
  20. "continue": {
  21. "sroffset": 10,
  22. "continue": "-||"
  23. },
  24. "query": {
  25. "searchinfo": {
  26. "totalhits": 36853
  27. },
  28. "search": [{
  29. "ns": 0,
  30. "title": "Stuff",
  31. "snippet": "<span></span>",
  32. "size": 1906,
  33. "wordcount": 204,
  34. "timestamp": "2016-06-10T17:25:36Z"
  35. }]
  36. }
  37. };
  38. describe('Wikipedia search service', () => {
  39. beforeEach(() => {
  40. TestBed.configureTestingModule({
  41. imports: [HttpModule],
  42. providers: [
  43. {
  44. provide: XHRBackend,
  45. useClass: MockBackend
  46. },
  47. SearchWiki
  48. ]
  49. });
  50. });
  51. it('should get search results', fakeAsync(
  52. inject([
  53. XHRBackend,
  54. SearchWiki
  55. ], (mockBackend: XHRBackend, searchWiki: SearchWiki) => {
  56. const expectedUrl = 'https://en.wikipedia.org/w/api.php?' +
  57. 'action=query&list=search&srsearch=Angular';
  58. mockBackend.connections.subscribe(
  59. (connection: MockConnection) => {
  60. expect(connection.request.method).toBe(RequestMethod.Get);
  61. expect(connection.request.url).toBe(expectedUrl);
  62. connection.mockRespond(new Response(
  63. new ResponseOptions({ body: mockResponse })
  64. ));
  65. });
  66. searchWiki.search('Angular')
  67. .subscribe(res => {
  68. expect(res).toEqual(mockResponse);
  69. });
  70. })
  71. ));
  72. it('should set foo with a 1s delay', fakeAsync(
  73. inject([SearchWiki], (searchWiki: SearchWiki) => {
  74. searchWiki.setFoo('food');
  75. tick(1000);
  76. expect(searchWiki.foo).toEqual('food');
  77. })
  78. ));
  79. });

View Example

We use inject to inject the SearchWiki service and the MockBackend into our test. We then wrap our entire test with a call to fakeAsync, which will be used to control the asynchronous behavior of the SearchWiki service for testing.

Next, we subscribe to any incoming connections from our back-end. This gives us access to an object MockConnection, which allows us to configure the response we want to send out from our back-end, as well as test any incoming requests from the service we're testing.

In our example, we want to verify that the SearchWiki's search method makes a GET request to the correct URL. This is accomplished by looking at the request object we get when our SearchWiki service makes a connection to our mock back-end. Analyzing the request.url property we can see if its value is what we expect it to be. Here we are only checking the URL, but in other scenarios we can see if certain headers have been set, or if certain POST data has been sent.

Now, using the MockConnection object we mock in some arbitrary data. We create a new ResponseOptions object where we can configure the properties of our response. This follows the format of a regular Angular Response class. Here we have simply set the body property to that of a basic search result set you might see from Wikipedia. We could have also set things like cookies, HTTP headers, etc., or set the status value to a non-200 state to test how our service responds to errors. Once we have our ResponseOptions configured we create a new instance of a Respond object and tell our back-end to start using this as a response by calling .mockRespond.

It is possible to use multiple responses. Say your service had two possible GET requests - one for /api/users, and another /api/users/1. Each of these requests has a different corresponding set of mock data. When receiving a new connection through the MockBackend subscription, you can check to see what type of URL is being requested and respond with whatever set of mock data makes sense.

Finally, we can test the search method of the SearchWiki service by calling it and subscribing to the result. Once our search process has finished, we check the result object to see if it contains the same data that we mocked into our back-end. If it is, then congratulations, your test has passed.

In the should set foo with a 1s delay test, you will notice that we call tick(1000) which simulates a 1 second delay.

原文: https://angular-2-training-book.rangle.io/handout/testing/services/mockbackend.html