React Native Keychain 的 Jest 单元测试指南

React Native Keychain 的 Jest 单元测试指南

react-native-keychain :key: Keychain Access for React Native react-native-keychain 项目地址: https://gitcode.com/gh_mirrors/re/react-native-keychain

前言

在 React Native 开发中,react-native-keychain 是一个常用的安全存储解决方案,它提供了安全的密码和凭证存储功能。然而,在进行单元测试时,由于 Jest 测试环境中没有原生模块的支持,直接使用该库会遇到问题。本文将详细介绍如何为 react-native-keychain 创建 Jest 模拟(mock),以便顺利进行单元测试。

为什么需要模拟 Keychain 模块

react-native-keychain 的核心功能依赖于原生平台的 Keychain/Keystore 服务,这些功能在纯 JavaScript 的 Jest 测试环境中是无法正常工作的。因此,我们需要创建模拟对象来替代真实的模块,这样可以在测试中:

  1. 避免调用实际的原生代码
  2. 控制函数的返回值以测试各种场景
  3. 验证函数是否被正确调用

创建 Keychain 模拟对象

首先,我们需要创建一个完整的模拟对象,它应该包含 react-native-keychain 模块的所有公共 API。以下是一个完整的模拟实现:

// keychainMock.ts

const keychainMock = {
  // 安全级别枚举
  SECURITY_LEVEL: {
    SECURE_SOFTWARE: 'MOCK_SECURITY_LEVEL_SECURE_SOFTWARE',
    SECURE_HARDWARE: 'MOCK_SECURITY_LEVEL_SECURE_HARDWARE',
    ANY: 'MOCK_SECURITY_LEVEL_ANY',
  },
  
  // 可访问性枚举
  ACCESSIBLE: {
    WHEN_UNLOCKED: 'MOCK_AccessibleWhenUnlocked',
    AFTER_FIRST_UNLOCK: 'MOCK_AccessibleAfterFirstUnlock',
    ALWAYS: 'MOCK_AccessibleAlways',
    WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 'MOCK_AccessibleWhenPasscodeSetThisDeviceOnly',
    WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'MOCK_AccessibleWhenUnlockedThisDeviceOnly',
    AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 'MOCK_AccessibleAfterFirstUnlockThisDeviceOnly',
  },
  
  // 访问控制枚举
  ACCESS_CONTROL: {
    USER_PRESENCE: 'MOCK_UserPresence',
    BIOMETRY_ANY: 'MOCK_BiometryAny',
    BIOMETRY_CURRENT_SET: 'MOCK_BiometryCurrentSet',
    DEVICE_PASSCODE: 'MOCK_DevicePasscode',
    APPLICATION_PASSWORD: 'MOCK_ApplicationPassword',
    BIOMETRY_ANY_OR_DEVICE_PASSCODE: 'MOCK_BiometryAnyOrDevicePasscode',
    BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE: 'MOCK_BiometryCurrentSetOrDevicePasscode',
  },
  
  // 认证类型枚举
  AUTHENTICATION_TYPE: {
    DEVICE_PASSCODE_OR_BIOMETRICS: 'MOCK_AuthenticationWithBiometricsDevicePasscode',
    BIOMETRICS: 'MOCK_AuthenticationWithBiometrics',
  },
  
  // 存储类型枚举
  STORAGE_TYPE: {
    FB: 'MOCK_FacebookConceal',
    AES: 'MOCK_KeystoreAESCBC',
    RSA: 'MOCK_KeystoreRSAECB',
    KC: 'MOCK_keychain',
  },
  
  // 核心方法模拟
  setGenericPassword: jest.fn().mockResolvedValue({
    service: 'mockService',
    storage: 'mockStorage',
  }),
  
  getGenericPassword: jest.fn().mockResolvedValue({
    username: 'mockUser',
    password: 'mockPassword',
    service: 'mockService',
    storage: 'mockStorage',
  }),
  
  resetGenericPassword: jest.fn().mockResolvedValue(true),
  hasGenericPassword: jest.fn().mockResolvedValue(true),
  
  getAllGenericPasswordServices: jest
    .fn()
    .mockResolvedValue(['mockService1', 'mockService2']),
  
  setInternetCredentials: jest.fn().mockResolvedValue({
    service: 'mockService',
    storage: 'mockStorage',
  }),
  
  getInternetCredentials: jest.fn().mockResolvedValue({
    username: 'mockUser',
    password: 'mockPassword',
    service: 'mockService',
    storage: 'mockStorage',
  }),
  
  resetInternetCredentials: jest.fn().mockResolvedValue(),
  getSupportedBiometryType: jest.fn().mockResolvedValue('MOCK_TouchID'),
  canImplyAuthentication: jest.fn().mockResolvedValue(true),
  getSecurityLevel: jest.fn().mockResolvedValue('MOCK_SECURE_SOFTWARE'),
};

export default keychainMock;

两种模拟配置方法

方法一:使用 __mocks__ 目录

这是 Jest 推荐的模块模拟方式,适用于全局模拟整个模块:

  1. 在项目根目录创建 __mocks__ 目录
  2. __mocks__ 目录下创建 react-native-keychain 子目录
  3. 在该子目录中创建 index.jsindex.ts 文件

文件内容如下:

// __mocks__/react-native-keychain/index.js

const keychainMock = {
  // 这里放置完整的模拟对象内容
  // 与上面创建的 keychainMock 相同
};

module.exports = keychainMock;

这种方法会自动应用于所有测试文件中对该模块的引用,无需在每个测试文件中单独设置。

方法二:使用 Jest 配置文件

如果你需要更灵活的模拟控制,可以通过 Jest 的配置文件来设置:

  1. jest.config.js 中添加 setup 文件配置:
module.exports = {
  // 其他配置...
  setupFiles: ['<rootDir>/jest.setup.js'],
};
  1. 创建 jest.setup.js 文件并添加以下内容:
// jest.setup.js

import keychainMock from './path/to/keychainMock';

jest.mock('react-native-keychain', () => keychainMock);

这种方法允许你在一个集中位置管理所有模拟设置,适合大型项目。

编写测试用例

有了模拟设置后,我们可以编写测试用例来验证使用了 react-native-keychain 的代码。以下是一些典型测试示例:

import Keychain from 'react-native-keychain';

describe('Keychain 功能测试', () => {
  beforeEach(() => {
    // 清除所有模拟函数的调用记录
    jest.clearAllMocks();
  });

  it('应该能保存和获取通用密码', async () => {
    // 测试保存密码
    await Keychain.setGenericPassword('testUser', 'testPassword');
    expect(Keychain.setGenericPassword).toHaveBeenCalledWith(
      'testUser',
      'testPassword'
    );

    // 测试获取密码
    const credentials = await Keychain.getGenericPassword();
    expect(credentials).toEqual({
      username: 'mockUser',
      password: 'mockPassword',
      service: 'mockService',
      storage: 'mockStorage',
    });
  });

  it('应该能检查密码是否存在', async () => {
    const exists = await Keychain.hasGenericPassword();
    expect(exists).toBe(true);
  });

  it('应该能重置密码', async () => {
    const reset = await Keychain.resetGenericPassword();
    expect(reset).toBe(true);
  });

  it('应该能获取支持的生物识别类型', async () => {
    const biometryType = await Keychain.getSupportedBiometryType();
    expect(biometryType).toBe('MOCK_TouchID');
  });
});

高级测试技巧

模拟不同的返回结果

你可以在特定测试中修改模拟函数的返回值:

it('应该处理获取密码失败的情况', async () => {
  // 修改模拟行为
  (Keychain.getGenericPassword as jest.Mock).mockResolvedValueOnce(false);
  
  const result = await Keychain.getGenericPassword();
  expect(result).toBe(false);
});

验证函数调用参数

it('应该使用正确的参数设置网络凭证', async () => {
  const server = 'example.com';
  const username = 'user';
  const password = 'pass';
  
  await Keychain.setInternetCredentials(server, username, password);
  
  expect(Keychain.setInternetCredentials).toHaveBeenCalledWith(
    server,
    username,
    password
  );
});

测试错误处理

it('应该处理保存密码时的错误', async () => {
  // 模拟抛出错误
  (Keychain.setGenericPassword as jest.Mock).mockRejectedValueOnce(
    new Error('保存失败')
  );
  
  await expect(Keychain.setGenericPassword('user', 'pass'))
    .rejects
    .toThrow('保存失败');
});

总结

通过本文介绍的方法,你可以轻松地为 react-native-keychain 创建 Jest 模拟,从而在单元测试中验证与安全存储相关的逻辑。记住:

  1. 完整的模拟应该包含所有枚举和方法
  2. 可以选择全局模拟或按需模拟
  3. 利用 Jest 的模拟功能可以测试各种场景
  4. 记得在测试之间清理模拟状态

良好的测试覆盖可以确保你的安全存储逻辑在各种情况下都能正常工作,而无需依赖实际的原生模块。

react-native-keychain :key: Keychain Access for React Native react-native-keychain 项目地址: https://gitcode.com/gh_mirrors/re/react-native-keychain

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林菁琚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值