使用 Jest 和 Bankrun 加速 Solana 程序测试

本指南将带你了解如何使用Bankrun测试你的Solana程序

测试你的 Solana 程序是开发过程中的关键部分,以确保你的程序按预期运行,甚至可以加快你的开发。本指南将带你了解如何使用 Bankrun 测试你的 Solana 程序,Bankrun 是一个超级快速的 Solana 程序测试运行器。

大多数 Solana 测试使用 Mocha 框架 编写测试,并使用 Chai 进行断言。不过,你可以使用任何你熟悉的测试框架。在本指南中,我们将看看 JestBankrun。使用 Bankrun,你可以将测试的速度提升近 10 倍,获得修改程序时间的能力,并编写自定义账户数据。

presets

有一些预设将为你的 Solana 程序设置一个基本的测试环境。这些预设例如:

    npx create-solana-dapp my-dapp

create-solana-dapp 将为你设置一个包含各种配置选项的 Solana Web 项目,包括 Next.js 或 React 客户端、Tailwind UI 库和一个简单的 Anchor 程序。测试使用 Jest 编写,可以通过 anchor test 命令运行。

npx create-solana-game my-game

create-solana-game 将为你设置一个包含 JestMochaBankrun 测试的 Solana 游戏项目,以及一个使用 Solana Wallet 适配器的 NextJS 应用和一个额外的 Unity 游戏引擎客户端。Mocha 和 Bankrun 测试都可以通过 anchor test 命令运行。

你还可以在 Solana Program Examples 中找到许多测试示例。

Anchor 测试

使用 Anchor 框架,你可以运行 anchor test 命令以执行 anchor.toml 文件中的预配置 test 命令。

    test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

这将在 tests 目录中使用 ts-mocha 命令运行测试,超时时间为 1,000,000 毫秒。

这里的 Anchor 功能是启动一个本地验证者,从你的 Anchor 工作区部署程序,并在 Anchor.toml 中定义的网络上运行测试。

提示:你也可以运行 anchor test --detach 让验证者在测试完成后继续运行,这样你就可以在 Solana Explorer 中检查你的交易。

你还可以在 anchor.toml 文件中定义你自己的测试命令。例如,你可以首先在本地验证者上运行 mocha 测试,然后使用 bankrun 运行 jest 测试,通过 && 将它们组合:

    test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts && yarn run jest"

这将首先运行 mocha 测试,然后运行 jest 测试。

你也可以定义你自己的测试命令。例如:

    super_test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 only_super_test.ts"
    jest_tests = "yarn test"

这些你将使用以下命令运行:

   anchor run super_test
   anchor run jest_tests

不过需要注意的是,在这种情况下,anchor 不会为你启动一个本地验证者。因此,你需要将程序部署到本地集群,或在公共测试网络上运行它们。另外,当通过 anchor test 命令运行时,Anchor 环境变量才会可用,而通过 yarn test 例如运行时则不可用。

从 Mocha 迁移到 Jest

在这一部分,我们将学习如何从 Mocha 迁移到 Jest。Jest 是一个类似于 Mocha 的 JavaScript 测试框架。它已经集成了测试运行器,因此你不再需要使用 Chai。

首先,你需要安装 Jest:

然后你在 package.json 中添加一个新命令:

    {
      "scripts": {
        "test": "jest"
      }
    }

然后你可以使用以下命令运行测试:

由于我们想用 Typescript 运行测试,因此需要安装 ts-jest 包,还需要创建一个 Jest 配置文件:

    yarn add --dev ts-jest @jest/globals @types/jest
    yarn ts-jest config:init

这将在你的项目中创建一个 jest.config.js 文件。现在你可以更新你的 Anchor.toml 文件以运行 Jest 测试:

Jest 故障排除

  1. 如果你收到 SyntaxError: Cannot use import statement outside a module
    错误,你可能没有创建 jest 配置,或者需要在你的 jest.config.js 文件中添加以下内容:
    module.exports = {
      transform: {
        "^.+\\.tsx?$": "ts-jest",
      },
      testEnvironment: "node",
      moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
    };
  1. 由于在本地验证者上运行测试可能需要相当长的时间,如果你收到错误消息表明测试完成后无法记录,则说明你的测试可能超时。与 Mocha 不同,Jest 没有默认的超时。你可以在 jest.config.js 文件中设置超时:
    module.exports = {
      testTimeout: 10000,
    };

或者你可以为单个测试设置超时:

    test("test name", async () => {
      // your test code
    }, 10000);
  1. 如果你收到错误消息表示缺少 Anchor 环境变量,可能是你尝试在没有通过 anchor testanchor run test 运行测试的情况下使用 AnchorProvider。只需更新你的 Anchor.toml 以运行 Jest 测试,yarn test 将在 Anchor 环境中运行,并设置所有环境变量。

Bankrun

你还可以使用 Solana Bankrun 代替 solana-test-validator。它的功能类似于本地验证者,但更轻便且更快。一些非常有用的功能是 编写自定义账户数据时间旅行,使得测试基于时间的程序和依赖特定账户数据的程序变得更加容易。

要使用 Bankrun 和 Bankrun Anchor,你需要将其添加到你的 package.json

    yarn add solana-bankrun anchor-bankrun

要将 Mocha Anchor 测试切换到 Bankrun Anchor 测试,你只需要将提供者更改为 BankrunProvider,并在每个测试文件中使用 startAnchor 创建上下文:

    // ... imports here
    describe('My test', () => {
      test('Test allocate counter + increment tx', async () => {
        const context = await startAnchor(".", [], []);
        const client = context.banksClient;

        const provider = new BankrunProvider(context);
        anchor.setProvider(provider);

        // ... testing logic
      }
    }

startAnchor 将自动将你的程序从 Anchor 工作区添加到 Bankrun 银行。你还可以通过在 startAnchor 函数中传递它们, 添加额外的账户和程序 到银行。不过,与在本地验证者上运行测试相比,还是有一些不同的地方,我们将在下一部分讨论。

Bankrun 与本地验证器的区别

Bankrun 使用 BanksServer(一个简化版的 Solana 的银行,用于处理交易和管理账户)和 BanksClient 来模拟 Solana 网络,使得可以使用时间操作和动态账户数据设置等高级测试功能。与 solana-test-validator 不同,bankrun 允许通过与网络状态的本地实例进行高效测试,因此非常适合在不需要完整验证器的情况下测试 Solana 程序。交易的行为非常相似,但与本地验证器有一些区别:

Airdrops

  • Bankrun 不支持空投。BankrunProvider 中使用的标准签名者将自动获得一些 Sol。如果你需要另一个已资助的账户,可以通过在 startAnchor 函数中传入一个额外的账户来创建一个。
    let secondKeypair: Keypair = new anchor.web3.Keypair();

    context = await startAnchor(
        "",[],
        [
        {
            address: secondKeypair.publicKey,
            info: {
            lamports: 1_000_000_000, // 1 SOL 等价
            data: Buffer.alloc(0),
            owner: SYSTEM_PROGRAM_ID,
            executable: false,
            },
        },
        ]
    );
    provider = new BankrunProvider(context);

确认交易

由于 Bankrun 直接在银行上工作,你不需要确认你的交易。因此 connection.confirmTransaction() 函数将不可用。你可以直接省略它。

获取账户数据

虽然你仍然可以使用 connection.getAccount 来检索账户数据,但在 bankrun 框架中,首选方法是使用 client.getAccount,它返回一个 Promise<Account>。这种方法与测试框架的设计更为一致。然而,如果你希望在其余的 Solana 代码库中保持一致,你可以继续使用 connection.getAccount。选择最适合你具体用例的方法。

    await client.getAccount(playerPDA).then(info => {
      const decoded = program.coder.accounts.decode(
        "playerData",
        Buffer.from(info.data),
      );
      console.log("玩家账户信息", JSON.stringify(decoded));
      expect(decoded).toBeDefined();
      expect(parseInt(decoded.energy)).toEqual(99);
    });

使用其他密钥对签署交易

默认情况下,当使用 program.function.rpc() 时,交易将自动使用 provider.wallet 密钥对进行签署。如果你想用其他密钥对签署交易,可以创建一个第二个提供者,然后使用那个提供者用其他密钥对签署交易。

    let secondKeypair: Keypair = new anchor.web3.Keypair();

    let context = await startAnchor(
    "",[],
    [
        {
            address: secondKeypair.publicKey,
            info: {
            lamports: 1_000_000_000,
            data: Buffer.alloc(0),
            owner: SYSTEM_PROGRAM_ID,
            executable: false,
            },
        },
        ]
    );
    beneficiaryProvider = new BankrunProvider(context);
    beneficiaryProvider.wallet = new NodeWallet(secondKeypair);

    secondProgram = new Program<Vesting>(IDL as Vesting, beneficiaryProvider);

在原生程序使用 Bankrun

你还可以将 Bankrun 用于 原生程序。主要区别是你使用 start 而不是 startAnchor 来启动 Bankrun 银行,然后可以使用 client 与银行进行交互。

    const context = await start(
      [{ name: "counter_solana_native", programId: PROGRAM_ID }],
      [],
    );
    const client = context.banksClient;

你可以使用 await client.processTransaction(tx) 来代替 program.instruction().rpc()

在 Solana 程序示例中,你可以找到一个 完整的原生 Bankrun 示例

Bankrun 故障排除

  1. 如果在使用 Bankrun 发送交易时遇到 Unknown action 'undefined' 错误,你可能尝试发送两个相同的交易,且使用相同的区块哈希。在发送第二个交易之前请求一个新的最近区块哈希,或者在你的指令中添加一些种子或参数,以确保它们会产生不同的交易哈希。

  2. 如果遇到 Clock handle timeout 错误,你只需重启终端并重新运行测试即可。

结论

测试你的 Solana 程序对于确保其按预期运行至关重要。Bankrun 提供了一个轻量和快速的替代方案,使你的测试速度提高到 10 倍。它支持自定义账户数据和时间旅行等强大功能,这可以显著增强你的测试能力。此外,Jest 是撰写测试的良好替代选择,并且可以轻松与 Bankrun 集成。

然而,需要注意一些使用 Bankrun 相较于本地验证器的缺点:

  1. 环境表现:使用 Bankrun 进行的测试可能无法完全代表实时或测试网环境。
  2. 代码重用性:在本地验证器测试中使用的某些代码在 Bankrun 中可能不可重用。
  3. 依赖性:使用 Bankrun 和 Bankrun Anchor 引入了特定于这些工具的依赖性。

尽管存在这些缺点,Bankrun 仍然是一个有价值的工具,可以极大地改善你的开发工作流程。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Solana中文社群
Solana中文社群
https://soldev.cn/