2019.07.12 20:09

Vue CLI创建的项目中使用axios-mock-adapter实现单元测试

在构建一定大小的SPA(单页面应用程序)时,会发生与几乎100%API服务器的通信。在这种情况下,通信通常使用HTTP / HTTPS,并且还在应用程序中使用HTTP客户端库。现在,使用这些库的模块的单元测试通常很困难。因为与您通信的服务器并不总是运行,结果还取决于整个过程中的网络条件。尝试测试通信失败的模式并不总是方便的。为了保持单元测试的质量不变,有必要解决这个问题,其中之一是使用模拟。

在这种情况下,使用axios-mock-adapter作为示例,我尝试了使用axios进行Vue应用程序的HTTP通信的情况来模拟HTTP通信部分的单元测试。


环境

此验证环境是以下环境。

$ node -v
v10.16.0

$ vue -V
3.9.2

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.5
BuildVersion:	18F132

其他主要模块版本如下。此外,如果未npm install -g @vue/cli安装vue命令,请安装它。

  • vue@2.6.10
  • axios@0.19.0
  • axios-mock-adapter@1.17.0
  • mocha@5.2.0
  • chai@4.2.0

创建项目

首先,使用Vue CLI创建项目。

$ vue create example-vue-axios-mock-adapter

将Preset设置为Manual并选择所有默认值。在这里,mocha和chai被选为测试套件。

选择最后一个项目时,安装开始。安装后,继续安装其他必需的模块。

$ cd example-vue-axios-mock-adapter
$ npm install axios
$ npm install -D axios-mock-adapter

UserService类的实现和测试用例的创建

在这种情况下,我们将实现一个管理公共用户信息CRUD的服务类,并为该类创建单元测试。

整个源代码也在GitHub上

首先,创建一个要测试的UserService类和一个此类处理的用户类型定义,如下所示。

export interface User {
  id: number | undefined;
  name: string;
  age: number;
}
import axios from 'axios';
import { User } from './types';

export default class UserService {
  
  public async findById(id: number): Promise<User> {
    const res = await axios.get(`/users/${id}`);
    return res.data;
  }

  public async register(user: User): Promise<User> {
    const res = await axios.post('/users', user);
    return res.data;
  }
  
}

用户是一个具有密钥ID,名称和年龄的简单类型。UserService提供了获取/保存此User类型值的方法。事实上,删除,更改,复杂搜索等可能是必要的,但这次我们只是简单地实现了这一点。

此外,作为虚构的API规范,在服务器端注册过程中自动分配用户ID。

让我们按如下方式实现这类测试:由于UserService本身很简单,测试也很简单。

/* tslint:disable:no-unused-expression */

import { expect } from 'chai';
import UserService from '@/services/UserService';
import { User } from '@/services/types';

describe('UserService', () => {
  let userService: UserService;

  beforeEach(() => {
    userService = new UserService();
  });

  it('findById with 1', async () => {
    const user = await userService.findById(1);
    expect(user.id).to.equal(1);
  });

  it('register user data', async () => {
    const data = {
      name: 'kaname madoka',
      age: 14,
    } as User;

    const user = await userService.register(data);
    expect(user.id).to.not.be.undefined;
    expect(user.name).to.equal(data.name);
    expect(user.age).to.equal(data.age);
  });
});

在findById的测试中,使用id = 1获取的对象的id也应该是1。寄存器的测试是id被震动(它没有被定义)并且它与除此之外传递的值相同。每个都经过测试。

好吧,让我们在这里进行一次单元测试。

WEBPACK  Compiled successfully in 4589ms

MOCHA  Testing...



 HelloWorld.vue
   ✓ renders props.msg when passed

 UserService
   1) findById with 1
   2) register user data


 1 passing (62ms)
 2 failing

 1) UserService
      findById with 1:
    Error: socket hang up
     at createHangUpError (_http_client.js:323:15)
     at Socket.socketOnEnd (_http_client.js:426:23)
     at endReadableNT (_stream_readable.js:1129:12)
     at process._tickCallback (internal/process/next_tick.js:63:19)

 2) UserService
      register user data:
    Error: socket hang up
     at createHangUpError (_http_client.js:323:15)
     at Socket.socketOnEnd (_http_client.js:426:23)
     at endReadableNT (_stream_readable.js:1129:12)
     at process._tickCallback (internal/process/next_tick.js:63:19)

是的,没有服务器可以与之通信,因此套接字挂起。实际上,如果没有真正的服务器实现,单元测试将无法成功。模拟库在这里很有用。让我们通过准备只返回值而不是与axios进行实际通信的halibote,在不改变UserService.ts实现的情况下成功进行测试。


axios mocking和测试用例纠正

让我们嘲笑axios。有几种方法可以做到这一点,但这次我们使用axios-mock-adapter模块。修改之前创建的测试用例。更改将以diff格式发布。

@@ -1,17 +1,27 @@
 /* tslint:disable:no-unused-expression */

 import { expect } from 'chai';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
 import UserService from '@/services/UserService';
 import { User } from '@/services/types';

 describe('UserService', () => {
   let userService: UserService;
+  let mockAxios: MockAdapter;

   beforeEach(() => {
     userService = new UserService();
+    mockAxios = new MockAdapter(axios);
   });

   it('findById with 1', async () => {
+    mockAxios.onGet('/users/1').reply(200, {
+      id: 1,
+      name: 'QB',
+      age: 18,
+    });
+
     const user = await userService.findById(1);
     expect(user.id).to.equal(1);
   });
@@ -22,6 +32,11 @@ describe('UserService', () => {
       age: 14,
     } as User;

+    mockAxios.onPost('/users').reply(201, {
+      id: 10,
+      ...data,
+    });
+
     const user = await userService.register(data);
     expect(user.id).to.not.be.undefined;
     expect(user.name).to.equal(data.name);

我会解释一些问题。

首先,在第15行,我通过beforeEach的过程嘲笑了axios。在内部,使用axios具有的适配器功能中断处理。

接下来在第19行,axios-mock-adapter 提供了一个名为onXxx的方法(Xxx对应于每个HTTP方法)。在这里,/users/1如果您尝试获取路径的请求,则将其设置为发送预定的响应。换句话说,如果没有与服务器实际通信,200 OK则其行为就像返回的内容一样。

第35行设置了响应以及第19行。在用户注册时,由于它在后端发出POST请求,因此它201 Created会向其返回内容。

让我们再次进行单元测试。

WEBPACK  Compiled successfully in 5012ms

MOCHA  Testing...



 HelloWorld.vue
   ✓ renders props.msg when passed

 UserService
   ✓ findById with 1
   ✓ register user data


 3 passing (35ms)

MOCHA  Tests completed successfully

这次成功了!


最后

我试过axios-mock-adapter。这次只测试普通系统,但是使用axios-mock-adapter,您还可以测试网络错误和超时等行为。如果我有机会,我会试试。



转载:https://dev.classmethod.jp/client-side/vue/vue-unittest-using-axios-mock-adapter/

DIY表情

(添加http或https协议)

提交评论

留言区 12

  1. 5
    @3sdfasdf😗😆😅😂😊😜😜😜😜😜😜😜😜😜😜😜😜😜😍😪😷😈❤️💖✈️☕️