为 React-Native 应用开玩笑测试 Animated.View

新手上路,请多包涵

我尝试使用 Jest for React-Native 测试 Animated.View 。当我将属性 visible 设置为 true 时,它应该使我的视图从 opacity 0opacity 1 .359– 动画化

这是我的组件呈现的内容:

 <Animated.View
    style={{
        opacity: opacityValue,
    }}
>
    <Text>{message}</Text>
</Animated.View>

其中 opacityValue 在道具 visible 更改时更新:

 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 时视图变得可见?

谢谢。

原文由 alexmngn 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 383
2 个回答

我通过为测试创建一个动画存根解决了这个问题。

我看到您正在使用 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();
});

现在生成的快照将具有预期的不透明度值。如果你经常使用动画,你可以将你的模拟移动到 js/config/jest 并编辑你的 package.json 以在你的所有测试中使用它,然后对你的存根所做的任何更改都将适用于所有测试。

编辑:

上面的解决方案只解决了从头到尾的问题。更细粒度的解决方案是:

  1. 不要嘲笑动画
  2. 在开玩笑的配置中使 global.requestAnimationFrame = null
  3. 使用 mockdate 模拟日期
  4. 使用 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);
};

由于动画内部行为,将步骤分成小块很重要。

原文由 Aspirina 发布,翻译遵循 CC BY-SA 3.0 许可协议

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)
  })
})

原文由 Matt McCann 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题