Non*_*Non 6 javascript testing ecmascript-6 reactjs jestjs
这是我第一次进行测试,并且获得了测试UI组件的技巧。现在,我正在尝试测试其中包含一些静态方法的类。它也包含参数。
见上课:
import UserInfoModel from '../models/UserInfo.model';
import ApiClient from './apiClient';
import ApiNormalizer from './apiNormalizer';
import Article from '../models/Article.model';
import Notification from '../models/Notification.model';
import Content from '../models/Link.model';
export interface ResponseData {
[key: string]: any;
}
export default class ApiService {
static makeApiCall(
url: string,
normalizeCallback: (d: ResponseData) => ResponseData | null,
callback: (d: any) => any
) {
return ApiClient.get(url)
.then(res => {
callback(normalizeCallback(res.data));
})
.catch(error => {
console.error(error);
});
}
static getProfile(callback: (a: UserInfoModel) => void) {
return ApiService.makeApiCall(`profile`, ApiNormalizer.normalizeProfile, callback);
}
}
Run Code Online (Sandbox Code Playgroud)
我已经创建了一个通过的小测试,但是我不确定自己在做什么。
// @ts-ignore
import moxios from 'moxios';
import axios from 'axios';
import { baseURL } from './apiClient';
import { dummyUserInfo } from './../models/UserInfo.model';
describe('apiService', () => {
let axiosInstance: any;
beforeEach(() => {
axiosInstance = axios.create();
moxios.install();
});
afterEach(() => {
moxios.uninstall();
});
it('should perform get profile call', done => {
moxios.stubRequest(`${baseURL.DEV}profile`, {
status: 200,
response: {
_user: dummyUserInfo
}
});
axiosInstance
.get(`${baseURL.DEV}profile`)
.then((res: any) => {
expect(res.status).toEqual(200);
expect(res.data._user).toEqual(dummyUserInfo);
})
.finally(done);
});
});
Run Code Online (Sandbox Code Playgroud)
我正在使用moxios测试axios的内容-> https://github.com/axios/moxios
那么哪种方法可以测试此类的正确方法呢?
Ser*_*ell 12
单元测试是由软件开发人员编写并运行的自动化测试,以确保应用程序的某个部分符合其设计并按预期运行。就像我们在谈论面向对象的编程一样,一个单元通常是一个完整的接口,例如一个类,但是可以是一个单独的方法。
单元测试的目的是隔离程序的每个部分,并表明各个部分是正确的。因此,如果我们考虑您的ApiService.makeApiCall职能:
static makeApiCall(
url: string,
normalizeCallback: (d: ResponseData) => ResponseData | null,
callback: (d: any) => any
) {
return ApiClient.get(url)
.then((res: any) => {
callback(normalizeCallback(res.data));
})
.catch(error => {
console.error(error);
});
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到它有一个ApiClient.get应该被嘲笑的外部资源调用。在这种情况下,模拟HTTP请求并不是完全正确的,因为ApiService不会直接利用它们,在这种情况下,您的单元变得比预期的要宽一些。
笑话框架提供了极大的机制嘲讽和例子OMAIR Nabiel是正确的。但是,我不仅喜欢对带有预定义数据的函数进行存根,而且还希望检查被存根的函数是否被调用了预期的次数(因此请使用模拟的真实本质)。因此,完整的模拟示例如下所示:
/**
* Importing `ApiClient` directly in order to reference it later
*/
import ApiClient from './apiClient';
/**
* Mocking `ApiClient` with some fake data provider
*/
const mockData = {};
jest.mock('./apiClient', function () {
return {
get: jest.fn((url: string) => {
return Promise.resolve({data: mockData});
})
}
});
Run Code Online (Sandbox Code Playgroud)
这允许向您的测试示例添加其他断言:
it('should call api client method', () => {
ApiService.makeApiCall('test url', (data) => data, (res) => res);
/**
* Checking `ApiClient.get` to be called desired number of times
* with correct arguments
*/
expect(ApiClient.get).toBeCalledTimes(1);
expect(ApiClient.get).toBeCalledWith('test url');
});
Run Code Online (Sandbox Code Playgroud)
因此,只要我们弄清楚了什么以及如何模拟数据,就让我们找出应该测试的内容。良好的测试应涵盖两种情况:积极测试 -通过提供有效数据来测试系统,以及消极测试 -通过提供无效数据来测试系统。以我的拙见,应该添加第三个分支- 边界测试 -测试,该测试的重点是要测试的软件的边界或限制条件。如果您对其他类型的测试感兴趣,请参考此词汇表。
因此,makeApiCall方法的正向测试流程应调用normalizeCallback和callback方法,我们可以按以下方式编写此测试(但是,有多种方法可以使猫变皮):
it('should call callbacks consequently', (done) => {
const firstCallback = jest.fn((data: any) => {
return data;
});
const secondCallback = jest.fn((data: any) => {
return data;
});
ApiService.makeApiCall('test url', firstCallback, secondCallback)
.then(() => {
expect(firstCallback).toBeCalledTimes(1);
expect(firstCallback).toBeCalledWith(mockData);
expect(secondCallback).toBeCalledTimes(1);
expect(secondCallback).toBeCalledWith(firstCallback(mockData));
done();
});
});
Run Code Online (Sandbox Code Playgroud)
请注意此测试中的几件事:- done由于此测试的异步性质,我正在使用回调来让测试知道已完成测试-我正在使用模拟了此mockData数据的变量,ApiClient.get因此我检查了该回调值正确- mockData类似的变量应该从...开始mock。否则,Jest将不允许其超出模拟范围
测试的负面方式看起来很相似。ApiClient.get方法应该抛出错误,ApiService应该处理并放入console。另外,我正在检查没有回调被调用。
import ApiService from './api.service';
const mockError = {message: 'Smth Bad Happened'};
jest.mock('./apiClient', function () {
return {
get: jest.fn().mockImplementation((url: string) => {
console.log('error result');
return Promise.reject(mockError);
})
}
});
describe( 't1', () => {
it('should handle error', (done) => {
console.error = jest.fn();
const firstCallback = jest.fn((data: any) => {
return data;
});
const secondCallback = jest.fn((data: any) => {
return data;
});
ApiService.makeApiCall('test url', firstCallback, secondCallback)
.then(() => {
expect(firstCallback).toBeCalledTimes(0);
expect(secondCallback).toBeCalledTimes(0);
expect(console.error).toBeCalledTimes(1);
expect(console.error).toBeCalledWith(mockError);
done();
});
});
});
Run Code Online (Sandbox Code Playgroud)
在您的情况下可能会进行边界测试,但是(根据类型定义normalizeCallback: (d: ResponseData) => ResponseData | null)只要可以返回第一个回调null,就可以检查是否成功转移到第二个回调而没有任何错误或异常,这是一个好习惯。我们可以稍微重写一下第二个测试:
it('should call callbacks consequently', (done) => {
const firstCallback = jest.fn((data: any) => {
return null;
});
const secondCallback = jest.fn((data: any) => {
return data;
});
ApiService.makeApiCall('test url', firstCallback, secondCallback)
.then(() => {
expect(firstCallback).toBeCalledTimes(1);
expect(firstCallback).toBeCalledWith(mockData);
expect(secondCallback).toBeCalledTimes(1);
done();
});
});
Run Code Online (Sandbox Code Playgroud)
关于测试异步代码,您可以在此处阅读全面的文档。主要思想是,当您具有异步运行的代码时,Jest需要知道它所测试的代码何时完成,然后才能继续进行其他测试。Jest提供了三种方法来实现此目的:
通过回调
it('the data is peanut butter', done => {
function callback(data) {
expect(data).toBe('peanut butter');
done();
}
fetchData(callback);
});
Run Code Online (Sandbox Code Playgroud)
Jest将等待直到完成的回调被调用,然后再完成测试。如果done()从不调用,则测试将失败,这就是您想要发生的情况。
通过承诺
如果您的代码使用Promise,则有一种更简单的方法来处理异步测试。只需从测试中返回一个承诺,Jest将等待该承诺解决。如果承诺被拒绝,则测试将自动失败。
async/await 句法
您可以在测试中使用async和await。要编写异步测试,只需async在传递给测试的函数前面使用关键字。
it('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
Run Code Online (Sandbox Code Playgroud)在这里,您可以找到代码的现成示例 https://github.com/SergeyMell/jest-experiments 请让我知道是否有您不清楚的地方。
关于你的问题
嗨,我该怎么做才能模拟./apiClient在同一文件中的成功和错误?
根据文档, Jest将自动将jest.mock调用提升到模块的顶部(在导入之前)。看来您可以做到setMock或doMock代替,但是,以这种方式嘲笑开发人员时常会遇到问题。可以使用require代替import和其他hack 来覆盖它们(请参阅本文),但是我不喜欢这种方式。
在这种情况下,对我来说正确的方法是拆分模拟的定义和实现,因此您声明将像这样模拟该模块
it('the data is peanut butter', done => {
function callback(data) {
expect(data).toBe('peanut butter');
done();
}
fetchData(callback);
});
Run Code Online (Sandbox Code Playgroud)
但是,根据测试范围,模拟功能的实现有所不同:
it('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
Run Code Online (Sandbox Code Playgroud)
api service据我所知,这将允许您将所有相关流存储在单个文件中。我已经更新了我的github例如用api.spec.ts它实现了上述所有。请看一下。
单元测试术语是不言而喻的,您可以测试一个单元。完全隔离的功能。任何外部依赖都被嘲笑。在这里,如果您要测试makeApiCall函数,则必须对它的参数进行存根,然后模拟ApiClient诺言,并期望该函数返回与模拟和存根参数有关的期望值。
人们通常忘记并且最重要的一件事是测试功能的否定情况。如果您的函数抛出错误将导致应用程序中断,将会发生什么。万一发生故障,函数的行为方式。编写测试是为了避免破坏应用程序中的更改。
这是一个更好的指南,如何在JEST中测试异步函数的编码示例:
https://www.leighhalliday.com/mocking-axios-in-jest-testing-async-functions
希望这可以帮助
更新
模拟您的ApiClient
对于通过情况:
jest.mock('./apiClient', () => {
get: jest.fn(() => Promise.resolve(data)) // for pass case
})
Run Code Online (Sandbox Code Playgroud)
失败案例:
jest.mock('./apiClient', () => {
get: jest.fn(() => Promise.reject(false)) // for fail case
})
Run Code Online (Sandbox Code Playgroud)
现在为这两种情况分别调用一次makeApiCall成功和失败。
失败案例:
const makeCall = await makeApiCall( <your stub params here> )
expect(makeCall).toThrowError() // note here you can check whatever you have done to handle error. ToThrowError is not a built-in function but just for understanding
Run Code Online (Sandbox Code Playgroud)
我大部分时间都在Jasmine中进行了测试,所以最后这段代码有点像伪代码。
| 归档时间: |
|
| 查看次数: |
483 次 |
| 最近记录: |