DOM Manipulation

Another class of functions that is often considered difficult to test is code that directly manipulates the DOM. Let’s see how we can test the following snippet of jQuery code that listens to a click event, fetches some data asynchronously and sets the content of a span.

  1. // displayUser.js
  2. 'use strict';
  3. const $ = require('jquery');
  4. const fetchCurrentUser = require('./fetchCurrentUser.js');
  5. $('#button').click(() => {
  6. fetchCurrentUser(user => {
  7. const loggedText = 'Logged ' + (user.loggedIn ? 'In' : 'Out');
  8. $('#username').text(user.fullName + ' - ' + loggedText);
  9. });
  10. });

接着,我们在__tests__/文件夹下创建一个测试文件:

  1. // __tests__/displayUser-test.js
  2. 'use strict';
  3. jest.mock('../fetchCurrentUser');
  4. test('displays a user after a click', () => {
  5. // Set up our document body
  6. document.body.innerHTML =
  7. '<div>' +
  8. ' <span id="username" />' +
  9. ' <button id="button" />' +
  10. '</div>';
  11. // This module has a side-effect
  12. require('../displayUser');
  13. const $ = require('jquery');
  14. const fetchCurrentUser = require('../fetchCurrentUser');
  15. // Tell the fetchCurrentUser mock function to automatically invoke
  16. // its callback with some data
  17. fetchCurrentUser.mockImplementation(cb => {
  18. cb({
  19. fullName: 'Johnny Cash',
  20. loggedIn: true,
  21. });
  22. });
  23. // Use jquery to emulate a click on our button
  24. $('#button').click();
  25. // Assert that the fetchCurrentUser function was called, and that the
  26. // #username span's inner text was updated as we'd expect it to.
  27. expect(fetchCurrentUser).toBeCalled();
  28. expect($('#username').text()).toEqual('Johnny Cash - Logged In');
  29. });

The function being tested adds an event listener on the #button DOM element, so we need to set up our DOM correctly for the test. Jest ships with jsdom which simulates a DOM environment as if you were in the browser. This means that every DOM API that we call can be observed in the same way it would be observed in a browser!

我们mock了 fetchCurrentUser.js的实现,这样我们的测试就不会产生真正的网络请求,而是使用本地mock的数据。 这确保了我们的测试能够在毫秒级完成,而不是秒,并且保证了快速的单元测试迭代速度。

这个例子的代码可以 examples/jquery找到。