如何使用 Jest 测试异步存储?

IT技术 reactjs asynchronous react-native jestjs asyncstorage
2021-03-29 13:29:51

我正在使用 React Native 构建一个应用程序。我想尽量减少与数据库通信的频率,因此我大量使用 AsyncStorage。但是,在 DB 和 AsyncStorage 之间的转换中存在很多错误空间。因此,我想通过运行自动化测试来确保 AsyncStorage 拥有我认为它拥有的数据。令人惊讶的是,我还没有在网上找到任何关于如何做到这一点的信息。我自己尝试这样做的尝试没有成功。

使用笑话:

it("can read asyncstorage", () => {
return AsyncStorage.getItem('foo').then(foo => {
  expect(foo).not.toBe("");
});  });

此方法失败并出现错误:

TypeError: RCTAsyncStorage.multiGet is not a function

删除返回值会导致它立即运行而无需等待值并错误地通过测试。

当我尝试使用 await 关键字对其进行测试时,遇到了完全相同的错误:

it('can read asyncstorage', async () => {
this.foo = "";
await AsyncStorage.getItem('foo').then(foo => {
    this.foo = foo;
});
expect(foo).not.toBe(""); });

关于如何针对 AsyncStorage 中的值成功运行断言的任何建议?我更愿意继续使用 Jest,但如果它只能使用一些替代测试库来完成,我对此持开放态度。

6个回答

对于在 2019 年以上看到此问题的每个人:

202011 月起AsyncStorage 已重命名为@react-native-async-storage/async-storage",如果您从react-native以下位置导入它,则会导致出现此警告

Warning: Async Storage has been extracted from react-native core and will be removed in a future release.

新module包含自己的模拟,因此您不必再担心自己编写。

根据项目的文档,您可以通过两种不同的方式进行设置:

##With 模拟目录

  • 在您的项目根目录中,创建一个__mocks__/@react-native-community目录。
  • 在该文件夹中,创建 async-storage.js 文件。
  • 在该文件中,导出异步存储模拟。
    export default from '@react-native-async-storage/async-storage/jest/async-storage-mock'
    

Jest 应该AsyncStorage在所有测试中默认模拟如果没有,请尝试jest.mock(@react-native-async-storage/async-storage)在测试文件的顶部调用

使用 Jest 安装文件

  • 在您的 Jest 配置中(可能在package.json或 中jest.config.js)添加安装文件的位置:
    "jest": {
      "setupFiles": ["./path/to/jestSetupFile.js"]
    }
    
  • 在您的设置文件中,设置 AsyncStorage 模拟:
    import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock';
    
    jest.mock('@react-native-community/async-storage', () => mockAsyncStorage);
    

如果您使用的是 TypeScript,则使用第二个选项(Jest 设置文件)会更容易,因为第一个选项(@types/react-native-community__async-storage模拟目录)不会自动与模拟关联

/jestasync-storage文件夹中没有看到任何文件夹:( jest 抛出一个错误,它可以找到您指定的路径
2021-05-26 13:29:51
@betoharres,您是使用@react-native-community/async-storageReact Native 还是内置的AsyncStorage
2021-06-06 13:29:51
注意:上述包不适用于世博项目:github.com/react-native-community/async-storage/issues/172
2021-06-06 13:29:51
@david-catillo 是的!我就像落后于他们的 1 个版本将模拟添加到库中。在这里参考我的解决方案:github.com/react-native-community/react-native-async-storage/...
2021-06-08 13:29:51
对于 Expo:这里有一种简单的方法可以在您的测试或 Jest 设置文件中删除 AsyncStorage: jest.mock("@react-native-community/async-storage", () => ({ AsyncStorage: { clear : jest.fn().mockName("clear"), getAllKeys: jest.fn().mockName("getAllKeys"), getItem: jest.fn().mockName("getItem"), removeItem: jest.fn( ).mockName("removeItem"), setItem: jest.fn().mockName("setItem") } }));
2021-06-15 13:29:51

也许你可以尝试这样的事情:

模拟存储.js

export default class MockStorage {
  constructor(cache = {}) {
    this.storageCache = cache;
  }

  setItem = jest.fn((key, value) => {
    return new Promise((resolve, reject) => {
      return (typeof key !== 'string' || typeof value !== 'string')
        ? reject(new Error('key and value must be string'))
        : resolve(this.storageCache[key] = value);
    });
  });

  getItem = jest.fn((key) => {
    return new Promise((resolve) => {
      return this.storageCache.hasOwnProperty(key)
        ? resolve(this.storageCache[key])
        : resolve(null);
    });
  });

  removeItem = jest.fn((key) => {
    return new Promise((resolve, reject) => {
      return this.storageCache.hasOwnProperty(key)
        ? resolve(delete this.storageCache[key])
        : reject('No such key!');
    });
  });

  clear = jest.fn((key) => {
    return new Promise((resolve, reject) =>  resolve(this.storageCache = {}));
  });

  getAllKeys = jest.fn((key) => {
    return new Promise((resolve, reject) => resolve(Object.keys(this.storageCache)));
  });
}

在您的测试文件中

import MockStorage from './MockStorage';

const storageCache = {};
const AsyncStorage = new MockStorage(storageCache);

jest.setMock('AsyncStorage', AsyncStorage)

// ... do things
我正在使用此答案作为解决方案,但我遇到了与 @jhm 相同的问题。任何进展?谢谢。
2021-05-31 13:29:51
应该被接受的答案,因为它最清楚地回答了起源问题
2021-06-08 13:29:51
尽管尝试从 'react-native' 导入,但从 'myFileName.js' ' 获取 'Cannot find module 'AsyncStorage' - 有什么想法吗?:)
2021-06-16 13:29:51
你是怎么清除的storageCache我试过了beforeEachawait AsyncStorage.clear() 但它不影响storageCache模拟的
2021-06-19 13:29:51

我最初的回答只是指出 react-native-simple-store 的作者是如何处理嘲笑的。我已经用我自己的模拟更新了我的答案,删除了 Jason 的硬编码模拟响应。

贾森奴有一个不错的简单的方法来此https://github.com/jasonmerino/react-native-simple-store/blob/master/测试/index-test.js#L31-L64

jest.mock('react-native', () => ({
AsyncStorage: {
    setItem: jest.fn(() => {
        return new Promise((resolve, reject) => {
            resolve(null);
        });
    }),
    multiSet:  jest.fn(() => {
        return new Promise((resolve, reject) => {
            resolve(null);
        });
    }),
    getItem: jest.fn(() => {
        return new Promise((resolve, reject) => {
            resolve(JSON.stringify(getTestData()));
        });
    }),
    multiGet: jest.fn(() => {
        return new Promise((resolve, reject) => {
            resolve(multiGetTestData());
        });
    }),
    removeItem: jest.fn(() => {
        return new Promise((resolve, reject) => {
            resolve(null);
        });
    }),
    getAllKeys: jest.fn(() => {
        return new Promise((resolve) => {
            resolve(['one', 'two', 'three']);
        });
    })
  }
}));

我自己的模拟:

const items = {};

jest.mock('react-native', () => ({

AsyncStorage: {        

    setItem: jest.fn((item, value) => {
        return new Promise((resolve, reject) => {        
    items[item] = value;
            resolve(value);
        });
    }),
    multiSet:  jest.fn((item, value) => {
        return new Promise((resolve, reject) => {
    items[item] = value;
            resolve(value);
        });
    }),
    getItem: jest.fn((item, value) => {
        return new Promise((resolve, reject) => {
            resolve(items[item]);
        });
    }),
    multiGet: jest.fn((item) => {
        return new Promise((resolve, reject) => {
            resolve(items[item]);
        });
    }),
    removeItem: jest.fn((item) => {
        return new Promise((resolve, reject) => {
            resolve(delete items[item]);
        });
    }),
    getAllKeys: jest.fn((items) => {
        return new Promise((resolve) => {
            resolve(items.keys());
        });
    })
  }
}));
@Ouadie 它需要编辑multiGetmultiSet定义。@brian-case 专门询问multiGet. 我将挖掘出我如何最终嘲笑我的,避免对resolve()
2021-05-29 13:29:51
@jaygooby,而不是items.keys()应该Object.keys(items)
2021-06-03 13:29:51
使用模拟时在我的测试中从“react-native-implementation.js”中获取“无法找到module“AsyncStorage” - 知道为什么吗?:) @jaygooby
2021-06-04 13:29:51
因为const items = {}The module factory of 'jest.mock()' is not allowed to reference any out-of-scope variables. Invalid variable access: items
2021-06-14 13:29:51
此解决方案仅适用于您的用例,下面的解决方案应该是公认的!
2021-06-21 13:29:51

我认为jest.setMock可能是在这种情况下比jest.mock更好,所以我们可以使用react-native没有问题只是嘲讽的AsyncStorage那样:

jest.setMock('AsyncStorage', {
  getItem: jest.fn(
    item =>
      new Promise((resolve, reject) => {
        resolve({ myMockObjectToReturn: 'myMockObjectToReturn' });
      })
  ),
});

使用以下命令安装module:从项目的根目录运行此命令。

npm install --save mock-async-storage

在项目根目录下创建__mocks__\@react-native-community文件夹。在里面创建一个文件 async-storage.js。async-storage.js 中的代码

export default from '@react-native-community/async-storage/jest/async-storage-mock'

在 package.json 中配置 jest 如下:

"jest": {
    "preset": "jest-expo",
    "transformIgnorePatterns" : ["/node_modules/@react-native-community/async-storage/(?!(lib))"]
  },

在这里,我使用 jest-expo 进行测试。如果您使用的是 jest,那么预设值将是 jest 而不是 jest-expo。

在项目根目录下,在 jest.config.js 文件中创建一个名为 jest.config.js 的配置文件:

module.exports = {
    setupFilesAfterEnv: [
      './setup-tests.js',
    ],
  };

在项目根目录中创建一个名为 setup-tests.js 的文件。该文件中的代码是:

import MockAsyncStorage from 'mock-async-storage';

const mockImpl = new MockAsyncStorage();
jest.mock('@react-native-community/async-storage', () => mockImpl);

在项目根目录下创建测试文件。这里我称它为 Example.test.js。

import  AsyncStorage  from '@react-native-community/async-storage';

beforeEach(() => {
    AsyncStorage.clear();
    // console.log(`After the data is being reset :`)
    // console.log(AsyncStorage)
});

it('can read asyncstorage', async () => {

    await AsyncStorage.setItem('username', 'testUser')
    let usernameValue = await AsyncStorage.getItem('username')
    // console.log(`After the data is being set :`)
    // console.log(AsyncStorage)
    expect(usernameValue).toBe('testUser')
});

这里使用 AsyncStorage.setItem用户名的值设置为testUser。然后使用 getItem 函数获取值。测试用例是比较usernameValue是否等于testUser。如果是,则测试用例通过,否则测试用例将失败。

使用 beforeEach 以便每次运行测试用例时 Asyncstorage 都被清除并且为空。如果需要,可以使用 console.log 检查 Asyncstorage 中存在的内容

运行命令yarn test来运行测试。输出是:

在此处输入图片说明