用玩笑测试登录 api

时间:2021-03-04 14:24:20

标签: javascript unit-testing jestjs

如何测试这个 Api 并获得 100% 的测试覆盖率?

const login = async (email, password) => {
  axios
    .post('https://conduit.productionready.io/api/users/login', {
      user: {
        email,
        password,
      },
    })
    .then((response) => response);
};

1 个答案:

答案 0 :(得分:2)

你的功能比较简单:一条路径,没有分支逻辑,一个外部调用。

您的函数所做的就是通过 axios.post 调用端点。

登录.js

export const login = async (email, password) => {
  /*
   * Notice that I added the 'await', else 'async' is useless.
   * Else you can directly return the axios.post method.
   */
  await axios
    .post('https://conduit.productionready.io/api/users/login', {
      user: {
        email,
        password,
      },
    })
    .then((response) => response); // This line is also useless for the moment
};

login.spec.js

import { login } from './login';

// Mock axios, else you will really request the endpoint
jest.mock('axios');
import axios from 'axios';

describe('Login tests', () => {
  describe('login function', () => {
    const email = 'test@test.com';
    const password = 'password';

    beforeEach(() => {
      /*
       * Not necessary for the moment, but will be useful
       * to test successful & error response
       */
      axios.post.mockResolvedValue({});
    });

    it('should call endpoint with given email & password', async () => {
      await login(email, password);
      expect(axios.post).toBeCalledWith(
        'https://conduit.productionready.io/api/users/login',
        { user: { email, password } },
      );
    });
  });
});

请注意,您可以通过返回某些内容并处理带有身份验证错误的错误来极大地改进您的登录功能。你的测试会更重要:

errors.js

export class DefaultError extends Error {
  static STATUS_CODE = 500; // You can change it, it depends how you use it
  name = 'DefaultError';

  constructor() {
    super('Default error, add what you want');
  }
}

export class AuthenticationError extends Error {
  static STATUS_CODE = 401;
  name = 'AuthenticationError';

  constructor() {
    super('Wrong credentials');
  }
}

登录.js

import { AuthenticationError, DefaultError } from './errors';

export const login = async (email, password) =>
  axios
    .post('https://conduit.productionready.io/api/users/login', {
      user: {
        email,
        password,
      },
    })
    .then(response => response.data)
    .catch(error => {
      // Handles the error how you want it
      if (error.status === AuthenticationError.STATUS_CODE) {
        throw new AuthenticationError();
      }
      throw new DefaultError();
    });

login.spec.js

import { login } from './login';
import { AuthenticationError, DefaultError } from './errors';

// Mock axios, else you will really request the endpoint
jest.mock('axios');
import axios from 'axios';

describe('Login tests', () => {
  describe('login function', () => {
    const email = 'test@test.com';
    const password = 'password';

    describe('with success', () => {
      const data = { something: {} };

      beforeEach(() => {
        axios.post.mockResolvedValue({ data });
      });

      it('should call endpoint with given email & password', async () => {
        await login(email, password);
        expect(axios.post).toBeCalledWith(
          'https://conduit.productionready.io/api/users/login',
          { user: { email, password } },
        );
      });

      it('should return response data', async () => {
        const response = await login(email, password);
        expect(response).toStrictEqual(data);
      });
    });

    describe('with error', () => {
      describe('status 401', () => {
        beforeEach(() => {
          axios.post.mockRejectedValue({ status: 401 });
        });

        it('should throw AuthenticationError', async () => {
          await expect(login(email, password)).rejects.toThrow(AuthenticationError);
        });
      });

      describe('other status', () => {
        beforeEach(() => {
          axios.post.mockRejectedValue({});
        });

        it('should throw DefaultError', async () => {
          await expect(login(email, password)).rejects.toThrow(DefaultError);
        });
      });
    });
  });
});

我们可以走得更远,但我认为你明白了。顺便说一句,您不需要像我一样拆分测试,我只是喜欢能够按所需的模拟对描述进行分组,并进行少量可读的测试。