Mocking APIs helps in situations where we only have the front-end of the application or we are dependant on third-party APIs. Mocking enables one to decouple the back-end from the front-end which results in faster execution of tests. In cypress, we can mock any XHR (XML Http Request) using cy.server() and cy.route(). Lets further deep dive by automating the below scenario –
1. Go to https://angular.realworld.io/
2. Now there are two XHR requests that are triggered when we open this page – Tags and Article Feed.
3. We will intercept the tags requests and instead of the original list of Tags, we will pass two completely new tags – cypress, selenium, and verify them in the UI.
4. We will intercept the Article Feed request and instead of the original list of articles, we will pass just one article with the changed username, description, and number of likes and then verify all of them in UI.
Step 1: Open https://angular.realworld.io/ and check the XHR requests from the dev tools.
When we open the tags XHR and check the response, we get the list of tags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | { "tags":[ "", "", "", "", "", "", "", "", "", "", "HuManIty", "HuManIty", "Gandhi", "HITLER", "SIDA", "BlackLivesMatter", "BlackLivesMatter", "test", "dragons", "butt" ] } |
Instead of the above response, we want that whenever the Tags XHR request is called the below response should be passed to the UI.
1 2 3 4 5 6 | { "tags":[ "cypress", "selenium" ] } |
We will add this to our fixtures files (tags.json).
Similarly, when we open the article feed XHR and check the response, we get the list of articles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | { "articles":[ { "title":"https://github.com/dudor", "slug":"https-github-com-dudor-mlbq5v", "body":"https://github.com/dudor", "createdAt":"2020-09-26T10:27:10.674Z", "updatedAt":"2020-09-26T10:27:10.674Z", "tagList":[ ], "description":"look here", "author":{ "username":"customer1", "bio":"https://github.com/dudor/my-learning/tree/master/react/realworld", "image":"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1120473411,2014836735\u0026fm=26\u0026gp=0.jpg", "following":false }, "favorited":false, "favoritesCount":0 }, { "title":"omygod", "slug":"omygod-qk0ds9", "body":"omygodomygod", "createdAt":"2020-09-26T10:26:20.519Z", "updatedAt":"2020-09-26T10:26:20.519Z", "tagList":[ ], "description":"omygod", "author":{ "username":"customer1", "bio":"https://github.com/dudor/my-learning/tree/master/react/realworld", "image":"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1120473411,2014836735\u0026fm=26\u0026gp=0.jpg", "following":false }, "favorited":false, "favoritesCount":0 }, { "title":"Test", "slug":"test-kwpnp7", "body":"Test 2", "createdAt":"2020-09-26T10:19:06.979Z", "updatedAt":"2020-09-26T10:19:06.979Z", "tagList":[ ], "description":"Test 1 ", "author":{ "username":"nicklay", "bio":null, "image":"https://static.productionready.io/images/smiley-cyrus.jpg", "following":false }, "favorited":false, "favoritesCount":0 }, { "title":"Test", "slug":"test-7uwhaf", "body":"Test", "createdAt":"2020-09-26T09:51:23.828Z", "updatedAt":"2020-09-26T09:51:23.828Z", "tagList":[ ], "description":"Test", "author":{ "username":"nicklay", "bio":null, "image":"https://static.productionready.io/images/smiley-cyrus.jpg", "following":false }, "favorited":false, "favoritesCount":1 }, { "title":"Sacrifice-Test", "slug":"sacrifice-test-3650bt", "body":"Sacrifice-Test", "createdAt":"2020-09-26T09:34:13.652Z", "updatedAt":"2020-09-26T09:34:13.652Z", "tagList":[ ], "description":"Sacrifice-Test", "author":{ "username":"Sacrifice", "bio":"6666", "image":"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3034308865,1649856130\u0026fm=26\u0026gp=0.jpg", "following":false }, "favorited":false, "favoritesCount":0 } ] } //I have removed some of the entries as the response was long. |
Instead of the above response, we want that whenever the Article XHR request is called the below response should be passed to the UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | { "articles":[ { "title":"Hi", "slug":"hi-5h5nba", "body":"testing", "createdAt":"2020-09-26T03:18:26.635Z", "updatedAt":"2020-09-26T03:18:26.635Z", "tagList":[ ], "description":"This is a test description", "author":{ "username":"testersdock", "bio":null, "image":"https://static.productionready.io/images/smiley-cyrus.jpg", "following":false }, "favorited":false, "favoritesCount":10 } ], "articlesCount":500 } |
In the above response, we are passing only one article, with username as testersdock, description as This is a test description and favoritesCount as 10. We will add this to our fixtures files (articlefeed.json).
Step 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | describe('Example to demonstrate API Mocking in Cypress', () => { beforeEach(() => { cy.server() cy.route('GET', '**/tags', 'fixture:tags.json') cy.route('GET', '**/articles*', 'fixture:articlefeed.json') cy.visit('https://angular.realworld.io/') }) it('Mock the Tags from the API Response and then validate on UI', () => { cy.get('.tag-list') .should('contain', 'cypress') .and('contain','selenium') }) it('Mock the Article feed from the API Response and then validate on UI', () => { cy.get('app-favorite-button.pull-xs-right').contains('10') cy.get('.author').contains('testersdock') cy.get('.preview-link > p').contains('This is a test description') }) }) |
1 2 3 4 5 6 | beforeEach(() => { cy.server() cy.route('GET', '**/tags', 'fixture:tags.json') cy.route('GET', '**/articles*', 'fixture:articlefeed.json') cy.visit('https://angular.realworld.io/') }) |
cy.server() starts a server to begin routing responses to cy.route() and to change the behavior of network requests.
cy.route(‘GET’, ‘**/tags’, ‘fixture:tags.json’) makes sure that that whenever the Tags api endpoint is called the response that is passed to the UI would be from tags.json fixture file.
cy.route(‘GET’, ‘**/articles*’, ‘fixture:articlefeed.json’) makes sure that that whenever the articles api endpoint is called the response that is passed to the UI would be from articlefeed.json fixture file.
1 2 3 4 5 6 | it('Mock the Tags from the API Response and then validate on UI', () => { cy.get('.tag-list') .should('contain', 'cypress') .and('contain','selenium') }) |
This will check that now there are only two tags displayed in the UI – cypress and selenium.
1 2 3 4 5 | it('Mock the Article feed from the API Response and then validate on UI', () => { cy.get('app-favorite-button.pull-xs-right').contains('10') cy.get('.author').contains('testersdock') cy.get('.preview-link > p').contains('This is a test description') }) |
This will check that now instead of a list of article, there is only one article with favoritesCount as 10, username as testersdock and description as This is a test description.
Step 3: After successful execution:
⚠️ Since cypress v6.0.0, cy.server() and cy.route() has been are deprecated and instead a new method has been introduced called cy.intercept(). I have rewritten this article using cy.intercept and you can find it here.
Do check out 🙂
Github: https://github.com/alapanme/Cypress-Automation
All Cypress Articles: https://testersdock.com/cypress-tutorial/