![](/img/trans.png)
[英]How to access the variable of a component constructor within unit tests with React-Native and Jest / Enzyme?
[英]How can I mock a service within a React component to isolate unit tests in jest?
我正在尝试重构一个单元测试,以从调用该服务的组件中隔离一个使用 axios 调用 API 的服务。
该服务目前非常简单:
import axios from 'axios'
export default class SomeService {
getObjects() {
return axios.get('/api/objects/').then(response => response.data);
}
}
下面是调用该服务的组件片段:
const someService = new SomeService();
class ObjectList extends Component {
state = {
data: [],
}
componentDidMount() {
someService.getObjects().then((result) => {
this.setState({
data: result,
});
});
}
render() {
// render table rows with object data
}
}
export default ObjectList
我可以测试 ObjectList 是否按照 mocking axios 的预期呈现数据:
// ...
jest.mock('axios')
const object_data = {
data: require('./test_json/object_list_response.json'),
};
describe('ObjectList', () => {
test('generates table rows from object api data', async () => {
axios.get.mockImplementationOnce(() => Promise.resolve(object_data));
const { getAllByRole } = render(
<MemoryRouter>
<table><tbody><ObjectList /></tbody></table>
</MemoryRouter>
);
await wait();
// test table contents
});
});
一切顺利通过。 作为一项主要的学术练习,我试图弄清楚如何模拟 SomeService 而不是 axios,这就是事情出错的地方,因为我认为我对传递的内部结构了解不够。
例如,我认为由于 SomeService 只返回 axios 响应,我可以类似地模拟 SomeService,有点像这样:
// ...
const someService = new SomeService();
jest.mock('./SomeService')
const object_data = {
data: require('./test_json/object_list_response.json'),
};
describe('ObjectList', () => {
test('generates table rows from object api data', async () => {
someService.getObjects.mockImplementationOnce(() => Promise.resolve(object_data))
// etc.
这失败并出现错误: Error: Uncaught [TypeError: Cannot read property 'then' of undefined]
,并且错误从ObjectList
追溯到这一行:
someService.getObjects().then((result) => {
我具体需要模拟什么,以便ObjectList
组件可以从SomeService
获得它需要的东西来设置它的 state?
在使用 jest 文档中建议的不同方法进行了一些试验和错误之后,似乎唯一可行的方法是使用模块工厂参数调用jest.mock()
,如下所示:
// rename data to start with 'mock' so that the factory can use it
const mock_data = {
data: require('./test_json/object_list_response.json'),
};
jest.mock('./SomeService', () => {
return jest.fn().mockImplementation(() => {
return {
getObjects: () => {
return Promise.resolve(mock_data).then(response => response.data)
}
};
});
});
// write tests
使用mockResolvedValue()
不起作用,因为我无法将 .then( .then()
链接出来。
如果这会导致任何人找到更优雅或惯用的解决方案,我会欢迎其他答案。
mocking class 实例的问题在于,在没有参考的情况下可能难以到达 class 实例及其方法。 由于someService
是组件模块的本地服务,因此无法直接访问。
如果没有特定的模拟, jest.mock('./SomeService')
依赖于以未指定方式工作的class 自动模拟。 该问题表明,模拟 class 的不同实例具有不同的getObjects
模拟方法,这些方法不会相互影响,尽管getObjects
是原型方法并且符合new SomeService().getObjects === new SomeService().getObjects
in unmocked class。
解决方案是不要依赖自动 mocking,而是让它按照预期的方式工作。 使模拟方法可以在 class 实例之外访问的一种实用方法是将其与模拟模块一起携带。 这样mockGetObjects.mockImplementationOnce
会影响现有的someService
。 mockImplementationOnce
暗示该方法可以在以后每次测试更改实现:
import { mockGetObjects }, SomeService from './SomeService';
jest.mock('./SomeService', () => {
let mockGetObjects = jest.fn();
return {
__esModule: true,
mockGetObjects,
default: jest.fn(() => ({ getObjects: mockGetObjects }))
};
});
...
mockGetObjects.mockImplementationOnce(...);
// instantiate the component
如果该方法应该具有恒定的模拟实现,这会简化任务,因为可以在jest.mock
中指定实现。 为断言公开mockGetObjects
可能仍然是有益的。
对于后代,另一种解决方案是在__mocks__
文件夹中创建手动模拟(受 Estus Flask 的评论和本文档的启发)。
./__mocks__/SomeService.js
export const mockGetObjects = jest.fn()
const mock = jest.fn(() => {
return {getObjects: mockGetObjects}
})
export default mock
然后简单的jest.mock('./SomeService')
调用与稍后在测试中定义的实现一起工作:
mockGetObjects.mockImplementationOnce(() => {
return Promise.resolve(object_data).then(response => response.data)
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.