React-Native 应用程序的 Jest 测试 Animated.View
Posted
技术标签:
【中文标题】React-Native 应用程序的 Jest 测试 Animated.View【英文标题】:Jest test Animated.View for React-Native app 【发布时间】:2017-07-05 06:04:00 【问题描述】:我尝试使用 Jest 为 React-Native 测试 Animated.View
。当我将属性visible
设置为true 时,它应该将我的视图从opacity 0
设置为opacity 1
。
这是我的组件呈现的内容:
<Animated.View
style=
opacity: opacityValue,
>
<Text>message</Text>
</Animated.View>
当道具visible
更改时,opacityValue
会更新:
Animated.timing(
this.opacityValue,
toValue: this.props.visible ? 1 : 0,
duration: 350,
,
).start(),
我想确保我的视图在我将其设置为属性visible=true
时可见。尽管视图需要一些时间才能变得可见,并且随着测试的运行,不透明度等于0
。
这是我的测试吧:
it('Becomes visible when visible=true', () =>
const tree = renderer.create(
<MessageBar
visible=true
/>
).toJSON();
expect(tree).toMatchSnapshot();
);
我想知道如何让 Jest 等待?或者我如何测试它以确保当我将道具设置为 true 时视图变得可见?
谢谢。
【问题讨论】:
【参考方案1】:我通过为测试创建一个动画存根解决了这个问题。
我看到您使用 visible 作为属性,所以一个工作示例是:
组件代码
import React from 'react';
import Animated, Text, View, TouchableOpacity from 'react-native';
// This class will control the visible prop
class AnimatedOpacityController extends React.Component
constructor(props, ctx)
super(props, ctx);
this.state =
showChild: false,
;
render()
const showChild = this.state;
return (
<View>
<AnimatedOpacity visible=this.state.showChild />
<TouchableOpacity onPress=() => this.setState( showChild: !showChild )>
<Text>showChild ? 'Hide' : 'Show' greeting</Text>
</TouchableOpacity>
</View>
);
// This is your animated Component
class AnimatedOpacity extends React.Component
constructor(props, ctx)
super(props, ctx);
this.state =
opacityValue: new Animated.Value(props.visible ? 1 : 0),
;
componentWillReceiveProps(nextProps)
if (nextProps.visible !== this.props.visible)
this._animate(nextProps.visible);
_animate(visible)
Animated.timing(this.state.opacityValue,
toValue: visible ? 1 : 0,
duration: 350,
).start();
render()
return (
<Animated.View style= opacity: this.state.opacityValue >
<Text>Hello World</Text>
</Animated.View>
);
export AnimatedOpacityController, AnimatedOpacity ;
现在开始测试
import React from 'react';
import renderer from 'react-test-renderer';
import shallow from 'enzyme';
import AnimatedOpacityController, AnimatedOpacity from '../AnimatedOpacity';
jest.mock('Animated', () =>
const ActualAnimated = require.requireActual('Animated');
return
...ActualAnimated,
timing: (value, config) =>
return
start: (callback) =>
value.setValue(config.toValue);
callback && callback()
,
;
,
;
);
it('renders visible', () =>
expect(
renderer.create(
<AnimatedOpacity visible=true />
).toJSON()
).toMatchSnapshot();
);
it('renders invisible', () =>
expect(
renderer.create(
<AnimatedOpacity visible=false />
).toJSON()
).toMatchSnapshot();
);
it('makes transition', () =>
const component = shallow(<AnimatedOpacityController />);
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
component.find('TouchableOpacity').simulate('press');
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
component.find('TouchableOpacity').simulate('press');
expect(renderer.create(component.node).toJSON()).toMatchSnapshot();
);
现在生成的快照将具有预期的不透明度值。
如果你经常使用动画,你可以将你的 mock 移动到 js/config/jest
并编辑你的 package.json 以在你的所有测试中使用它,那么对你的存根所做的任何更改都将适用于所有测试。
已编辑:
上面的解决方案只解决了从头到尾的问题。更细化的解决方案是:
-
不要嘲笑动画
在开玩笑的配置中制作
global.requestAnimationFrame = null
使用 mockdate 模拟日期
使用 jest.runTimersToTime 进行时间旅行
时间旅行函数是
const timeTravel = (ms, step = 100) =>
const tickTravel = v =>
jest.runTimersToTime(v);
const now = Date.now();
MockDate.set(new Date(now + v));
let done = 0;
while (ms - done > step)
tickTravel(step);
done += step;
tickTravel(ms - done);
;
由于动画内部行为,将步骤分成小块很重要。
【讨论】:
遇到了同样的问题,无法让它工作,你能分享一个示例项目吗?你能解释一下为什么有必要设置 global.requestAnimationFrame = null 并模拟日期吗?谢谢 它不起作用,因为从react-native
导入时无法模拟导入:`Cannot find module 'Animated from 'BicolorFavoriteCount.test.tsx'`【参考方案2】:
Aspirina 的 EDIT 有助于解决这个问题,但它并没有直接完成工作。对于后面的人,这就是我解决模拟动画进度问题的方法:
我正在使用 Jest - 这是我的 setupTests.js 脚本,用于引导测试环境
const MockDate = require('mockdate')
const frameTime = 10
global.requestAnimationFrame = (cb) =>
// Default implementation of requestAnimationFrame calls setTimeout(cb, 0),
// which will result in a cascade of timers - this generally pisses off test runners
// like Jest who watch the number of timers created and assume an infinite recursion situation
// if the number gets too large.
//
// Setting the timeout simulates a frame every 1/100th of a second
setTimeout(cb, frameTime)
global.timeTravel = (time = frameTime) =>
const tickTravel = () =>
// The React Animations module looks at the elapsed time for each frame to calculate its
// new position
const now = Date.now()
MockDate.set(new Date(now + frameTime))
// Run the timers forward
jest.advanceTimersByTime(frameTime)
// Step through each of the frames
const frames = time / frameTime
let framesEllapsed
for (framesEllapsed = 0; framesEllapsed < frames; framesEllapsed++)
tickTravel()
这里的想法是,我们将 requestAnimationFrame 速率减慢到恰好 100 fps,并且 timeTravel 函数允许您以一帧的时间增量前进。这是一个如何使用它的示例(假设我有一个动画需要一秒钟才能完成):
beforeEach(() =>
// As part of constructing the Animation, it will grab the
// current time. Mocking the date right away ensures everyone
// is starting from the same time
MockDate.set(0)
// Need to fake the timers for timeTravel to work
jest.useFakeTimers()
)
describe('half way through animation', () =>
it('has a bottom of -175', () =>
global.timeTravel(500)
expect(style.bottom._value).toEqual(-175)
)
)
describe('at end of animation', () =>
it('has a bottom of 0', () =>
global.timeTravel(1000)
expect(style.bottom._value).toEqual(0)
)
)
【讨论】:
工作愉快。谢谢 当单个js文件中有多个动画时,这将不起作用【参考方案3】:就我而言,我根本没有使用Animated.View
。但相反,我有一个使用requestAnimationFrame
的组件。回调实际上使用了 time
参数,所以我必须在替换 requestAnimationFrame
时将当前时间传递给回调函数,如下所示:
global.requestAnimationFrame = (cb) =>
setTimeout(() => cb(Date.now()), frameTime)
【讨论】:
【参考方案4】:您可以模拟Animated.View
,使其在测试时表现得像一个常规视图。
jest.mock('react-native', () =>
const rn = jest.requireActual('react-native')
const spy = jest.spyOn(rn.Animated, 'View', 'get')
spy.mockImplementation(() => jest.fn((children) => children));
return rn
);
我改编自 React Transition Group 的 example of mocking Transition Groups
【讨论】:
【参考方案5】:您可以通过以下方式模拟 Animated.createAnimatedComponent
jest.mock('react-native', () =>
const rn = jest.requireActual('react-native');
const spy = jest.spyOn(rn.Animated, 'createAnimatedComponent');
spy.mockImplementation(() => jest.fn(() => null));
return rn;
);
【讨论】:
以上是关于React-Native 应用程序的 Jest 测试 Animated.View的主要内容,如果未能解决你的问题,请参考以下文章
react-native, jest, ts-jest: ReferenceError: React is not defined
使用 Jest 在 React-Native 中测试组件行为(点击)
如何在 React-Native 项目中使用 Jest 测试样式化组件?
使用 Jest 运行 testing-library/react-native 时 RNEncryptedStorage 未定义