lottie-react-native单元测试高级技巧:模拟动画加载与事件

【免费下载链接】lottie-react-native 【免费下载链接】lottie-react-native 项目地址: https://gitcode.com/gh_mirrors/lot/lottie-react-native

你是否还在为动画组件的单元测试头疼?本文将系统讲解如何解决lottie-react-native测试中的三大痛点:动画加载超时、事件回调验证、跨平台兼容性测试,帮助你构建稳定可靠的动画测试体系。读完本文你将掌握:使用Jest模拟本地/远程动画资源、验证onAnimationFinish等关键事件、编写跨平台兼容的测试用例。

测试环境准备

核心依赖配置

lottie-react-native的测试需要配置React Native测试环境并安装必要依赖:

# 安装测试依赖
npm install --save-dev jest react-test-renderer @testing-library/react-native

关键文件结构

测试相关的核心源码文件路径:

模拟动画资源加载

本地JSON动画模拟

使用Jest的jest.mock模拟本地JSON动画文件,避免真实文件读取:

// 模拟本地动画文件
jest.mock('../animations/LottieLogo1.json', () => ({
  v: '5.5.7',
  fr: 30,
  ip: 0,
  op: 60,
  w: 300,
  h: 300,
  layers: []
}), { virtual: true });

// 测试组件中使用
import LottieLogo1 from '../animations/LottieLogo1.json';

test('renders local animation', () => {
  const { getByTestId } = render(<LottieView source={LottieLogo1} testID="local-animation" />);
  expect(getByTestId('local-animation')).toBeTruthy();
});

远程URI动画模拟

使用jest.mock结合jest.fn模拟LottieView组件,拦截远程资源加载:

// 模拟LottieView组件
jest.mock('lottie-react-native', () => ({
  __esModule: true,
  default: jest.fn(({ source, onAnimationLoaded }) => {
    // 模拟远程加载完成
    if (source?.uri && onAnimationLoaded) {
      setTimeout(onAnimationLoaded, 0);
    }
    return <View testID="mock-lottie" />;
  })
}));

test('handles remote animation loading', async () => {
  const onLoaded = jest.fn();
  render(<LottieView source={{ uri: 'https://example.com/anim.json' }} onAnimationLoaded={onLoaded} />);
  
  // 验证加载完成回调被调用
  await waitFor(() => {
    expect(onLoaded).toHaveBeenCalled();
  });
});

.lottie格式文件处理

针对二进制.lottie格式文件,使用jest.mock直接返回模拟对象:

// 模拟.lottie文件
jest.mock('../animations/animation_lkekfrcl.lottie', () => 'mock-lottie-file', { virtual: true });

test('renders dotLottie animation', () => {
  const { getByTestId } = render(
    <LottieView source={require('../animations/animation_lkekfrcl.lottie')} testID="dotlottie-animation" />
  );
  expect(getByTestId('dotlottie-animation')).toBeTruthy();
});

事件回调测试策略

onAnimationFinish事件验证

通过模拟动画完成事件,验证回调函数行为:

test('triggers onAnimationFinish when loop is false', async () => {
  const finishCallback = jest.fn();
  
  render(
    <LottieView
      source={LottieLogo1}
      loop={false}
      autoPlay={true}
      onAnimationFinish={finishCallback}
      testID="finish-test"
    />
  );
  
  // 等待动画完成回调
  await waitFor(() => {
    expect(finishCallback).toHaveBeenCalledWith(false); // isCancelled=false
  });
});

onAnimationFailure错误处理

模拟资源加载失败场景,测试错误处理逻辑:

test('handles animation loading failure', async () => {
  const errorCallback = jest.fn();
  
  // 模拟加载失败的LottieView
  jest.mock('lottie-react-native', () => ({
    __esModule: true,
    default: jest.fn(({ onAnimationFailure }) => {
      // 立即触发错误回调
      setTimeout(() => onAnimationFailure?.('Failed to load'), 0);
      return <View testID="failing-lottie" />;
    })
  }));

  render(
    <LottieView
      source={{ uri: 'invalid-url' }}
      onAnimationFailure={errorCallback}
    />
  );
  
  await waitFor(() => {
    expect(errorCallback).toHaveBeenCalledWith('Failed to load');
  });
});

事件流程测试

使用 Jest 的 mock 函数跟踪完整事件流程:

test('complete animation event flow', async () => {
  const loadCallback = jest.fn();
  const finishCallback = jest.fn();
  
  render(
    <LottieView
      source={LottieLogo1}
      loop={false}
      autoPlay={true}
      onAnimationLoaded={loadCallback}
      onAnimationFinish={finishCallback}
    />
  );
  
  // 验证事件触发顺序
  await waitFor(() => {
    expect(loadCallback).toHaveBeenCalledTimes(1);
  });
  
  await waitFor(() => {
    expect(finishCallback).toHaveBeenCalledWith(false);
  });
  
  // 确保加载先于完成
  expect(loadCallback.mock.invocationCallOrder[0]).toBeLessThan(
    finishCallback.mock.invocationCallOrder[0]
  );
});

跨平台测试注意事项

平台特定属性处理

针对不同平台的特有属性,使用Platform.select在测试中区分处理:

import { Platform } from 'react-native';

test('applies platform-specific props', () => {
  const mockLottie = jest.fn();
  jest.mock('lottie-react-native', () => ({
    __esModule: true,
    default: mockLottie
  }));
  
  render(<LottieView 
    enableMergePathsAndroidForKitKatAndAbove={true} 
    useNativeLooping={Platform.OS === 'windows'} 
  />);
  
  if (Platform.OS === 'android') {
    expect(mockLottie).toHaveBeenCalledWith(
      expect.objectContaining({
        enableMergePathsAndroidForKitKatAndAbove: true
      }),
      expect.anything()
    );
  }
});

渲染模式测试

验证不同渲染模式(AUTOMATIC/HARDWARE/SOFTWARE)的属性传递:

test('sets render mode correctly', () => {
  const { rerender } = render(<LottieView renderMode="HARDWARE" testID="lottie-render" />);
  expect(getByTestId('lottie-render').props.renderMode).toBe('HARDWARE');
  
  // 测试模式切换
  rerender(<LottieView renderMode="SOFTWARE" testID="lottie-render" />);
  expect(getByTestId('lottie-render').props.renderMode).toBe('SOFTWARE');
});

高级测试场景

进度控制测试

模拟进度值变化,验证动画状态更新:

test('controls animation progress', () => {
  const { rerender } = render(<LottieView source={LottieLogo1} progress={0} testID="progress-test" />);
  
  // 更新进度值
  rerender(<LottieView source={LottieLogo1} progress={0.5} testID="progress-test" />);
  expect(getByTestId('progress-test').props.progress).toBe(0.5);
  
  rerender(<LottieView source={LottieLogo1} progress={1} testID="progress-test" />);
  expect(getByTestId('progress-test').props.progress).toBe(1);
});

颜色滤镜测试

验证颜色滤镜属性正确应用:

test('applies color filters', () => {
  const colorFilters = [
    { keypath: 'layer1', color: '#ff0000' }
  ];
  
  const { getByTestId } = render(
    <LottieView 
      source={LottieLogo1} 
      colorFilters={colorFilters} 
      testID="color-filter-test" 
    />
  );
  
  expect(getByTestId('color-filter-test').props.colorFilters).toEqual(
    colorFilters.map(filter => ({
      ...filter,
      color: processColor(filter.color) // 验证颜色值被处理
    }))
  );
});

测试工具与最佳实践

测试工具链

推荐使用以下工具组合提升测试效率:

  • 组件测试@testing-library/react-native - 提供直观的组件查询和交互API
  • 异步测试jest.useFakeTimers()结合waitFor - 处理动画异步事件
  • 代码覆盖率jest --coverage - 确保测试覆盖关键逻辑路径

常见问题解决方案

问题场景 解决方法
动画加载超时 使用jest.setTimeout增加超时时间,或直接模拟加载完成
事件不触发 确保loop={false}时测试onAnimationFinish,避免循环动画无法结束
跨平台差异 使用Platform.OS条件测试,分别验证各平台特有属性

性能优化建议

  • 减少渲染次数:使用jest.mock替代真实组件渲染
  • 并行测试:通过jest --maxWorkers=4启用多线程测试
  • 选择性测试:使用test.onlytest.skip聚焦关键测试用例

总结

通过本文介绍的测试技巧,你可以:

  1. 使用Jest模拟各种动画资源(本地JSON、远程URI、.lottie格式)
  2. 验证关键事件回调(加载完成、播放结束、错误处理)
  3. 处理跨平台差异和特殊渲染模式
  4. 编写高效可靠的动画组件测试用例

官方文档:docs/api.md 提供了完整的API参考,建议结合测试实践深入学习。通过系统化的单元测试,可以显著提升动画功能的稳定性和开发效率。

掌握这些技巧,让你的动画组件测试不再依赖真实设备和网络环境,实现快速、可靠的测试流程!

【免费下载链接】lottie-react-native 【免费下载链接】lottie-react-native 项目地址: https://gitcode.com/gh_mirrors/lot/lottie-react-native

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐