我如何使用Jest测试带有DOM元素的JavaScript代码

时间:2018-08-18 14:41:02

标签: jestjs

是javascript的新手,我一直在尝试为他的代码编写测试,但是我却没有能力,我们应该使用笑话。 我已经研究了很长时间,没有找到解决方案。

document.getElementById("signup").addEventListener("submit", function(e) {
  e.preventDefault();
  data = {
    username: document.getElementById("username").value,
    email: document.getElementById("email").value,
    password: document.getElementById("password").value,
    confirm_password: document.getElementById("confirmPassword").value,
  };
});


signUp = (data) => {

    fetch("https://diaryapi-v2.herokuapp.com/mydiary/v1/auth/register", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
      })
      .then(res => res.json())

      .then(data => {
        if (data.message === "Your account was created") {
          let msg = data.message;
          document.getElementById("white").innerHTML = msg;
          window.location.href = "/signin";
        } else {
          let msg = Object.values(data);
          console.log(msg)
          document.getElementById("white").innerHTML = msg;
        }

      })
      .catch(error => console.error("Error:", error));

}

1 个答案:

答案 0 :(得分:1)

您的代码没有任何导出,因此您需要在测试运行时使用require()

它发出网络请求并设置窗口位置,因此您将需要使用Mocks Functions来验证它是否按预期工作,而无需实际发出网络请求和设置窗口位置。

它通过使用给then()的回调来异步执行一些工作,因此您需要在测试中加以考虑。

Jest使用jsdom在单元测试中提供类似于浏览器的环境,您可以使用它来设置所有内容,然后再使用require()运行代码。

我对以下注释中提到的您的代码做了一些小的修改:


code.js

document.getElementById("signup").addEventListener("submit", function (e) {
  e.preventDefault();
  // add 'const'
  const data = {
    username: document.getElementById("username").value,
    email: document.getElementById("email").value,
    password: document.getElementById("password").value,
    confirm_password: document.getElementById("confirmPassword").value,
  };
  signUp(data); // call signUp with the data
});

// add 'const'
const signUp = (data) => {

  fetch("https://diaryapi-v2.herokuapp.com/mydiary/v1/auth/register", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(data)
  })
    .then(res => res.json())

    .then(data => {
      if (data.message === "Your account was created") {
        let msg = data.message;
        document.getElementById("white").innerHTML = msg;
        window.location.assign("/signin"); // change this to assign() so it can be mocked
      } else {
        let msg = Object.values(data);
        console.log(msg)
        document.getElementById("white").innerHTML = msg;
      }

    })
    .catch(error => console.error("Error:", error));

}

这是上述代码的有效测试,可以用作参考:

code.test.js

describe('code', () => {

  let fetchMock;
  let assignMock;

  beforeEach(() => {
    // Jest uses jsdom as the default test environment which emulates
    // a browser and provides a document object for the unit tests.
    // Initialize the document body with the HTML needed for the tests
    document.body.innerHTML += `
      <form id="signup">
        <input type="text" id="username" value="the username">
        <input type="text" id="email" value="the email">
        <input type="text" id="password" value="the password">
        <input type="text" id="confirmPassword" value="the confirmPassword">
        <input type="submit" id="submitbutton">
      </form>
      <div id="white"></div>
    `;
    // Create a mock for fetch and provide a mock implementation
    // so the unit tests aren't actually making network requests
    fetchMock = jest.spyOn(global, 'fetch');
    fetchMock.mockImplementation(() => Promise.resolve({
      json: () => Promise.resolve({ message: 'Your account was created' })
    }));
    // Create a mock for window.location.assign()
    // so the unit tests aren't actually changing the window location
    assignMock = jest.spyOn(window.location, 'assign');
    assignMock.mockImplementation(() => {});
    // With everything set up, require the code
    require('./code');
  });

  afterEach(() => {
    // After each test call mockRestore() to restore the original functions
    fetchMock.mockRestore();
    assignMock.mockRestore();
    // resetModules() resets the module registry in Jest and ensures
    // a fresh copy of './code' executes on require()
    jest.resetModules();
  });

  it('should fetch data, change contents of #white, and change the page location on submit', async () => {
    // Submit the form
    document.getElementById('submitbutton').click();

    // Check that fetch was called with the expected arguments
    expect(fetchMock).toHaveBeenCalledTimes(1);
    const fetchArgs = fetchMock.mock.calls[0];
    expect(fetchArgs[0]).toBe('https://diaryapi-v2.herokuapp.com/mydiary/v1/auth/register');
    expect(fetchArgs[1]).toEqual({
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        username: 'the username',
        email: 'the email',
        password: 'the password',
        confirm_password: 'the confirmPassword',
      })
    });

    // pause synchronous execution of the test for two event loop cycles
    // so the callbacks queued by the then()'s within signUp have a chance to run    
    await Promise.resolve().then();

    // Check that window.location.assign() was called with the expected arguments
    expect(assignMock).toHaveBeenCalledTimes(1);
    expect(assignMock.mock.calls[0][0]).toBe('/signin');

    // Check that #white was updated
    expect(document.getElementById('white').innerHTML).toBe('Your account was created');
  });

});