如何在Flutter中使用Mockito测试这段异步的代码?

huangapple go评论48阅读模式
英文:

How can I test this async piece of code in Flutter using Mockito?

问题

I am trying to write some test for an app, but I can't test values stored in a provider. I am relatively new to testing, so there might be something that I am doing wrong, but anyway. What I want to test is to verify is two values are not the same, which should be the expected behavior, but I just can't make it pass. This is the code that I want to test:

class RSAKeysProvider {
    KeyPair? _keyPair;

    KeyPair? get keyPair => _keyPair;
    set setKeyPair(KeyPair keyPair) => _keyPair = keyPair;

    Future<void> generate(String bits) async {
        var keyPair = await RSA.generate(int.parse(bits));
        _keyPair = keyPair;
        notifyListeners();
    }
}

I need to first call the generate() function, which will set the keyPair to actual values, and then check if keyPair.publicKey is different than keyPair.privateKey, but it gives me an error when I try to call generate() with await inside a test.

This is what I have for now, but it doesn't work. The test breaks when it comes to the line "await rsaKeys.generate('2048'). What can I do to make it work? I know the condition is not checking if both are different, but it is just a placeholder, I can't make the code arrive there!

test('Public and private key should be different', () async {
      final MockRSAKeysProvider rsaKeys = MockRSAKeysProvider();

      when(rsaKeys.generate(any)).thenAnswer((value) async {
        KeyPair keyPair = await RSA.generate(2048);
        rsaKeys.setKeyPair = keyPair;
      });

      await rsaKeys.generate('2048');

      expect(rsaKeys.keyPair?.privateKey, isNotNull);
      expect(rsaKeys.keyPair?.publicKey, isNotNull);
    });

When it arrives at "await rsaKeys.generate('2048'), it gives me this error:

Invalid argument(s): Failed to load dynamic library 'librsa_bridge.dylib': dlopen(librsa_bridge.dylib, 0x0001): tried: 'librsa_bridge.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibrsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib' (no such file), '/usr/lib/librsa_bridge.dylib' (no such file, not in dyld cache), 'librsa_bridge.dylib' (no such file), '/usr/lib/librsa_bridge.dylib' (no such file, not in dyld cache)
  dart:ffi                                     new DynamicLibrary.open
  package:fast_rsa/bridge/binding.dart 117:33  Binding.openLib
  package:fast_rsa/bridge/binding.dart 26:16   new Binding._internal
  package:fast_rsa/bridge/binding.dart 17:45   Binding._singleton
  package:fast_rsa/bridge/binding.dart         Binding._singleton
  package:fast_rsa/bridge/binding.dart 22:12   new Binding
  package:fast_rsa/fast_rsa.dart 40:32         RSA.bindingEnabled
  package:fast_rsa/fast_rsa.dart               RSA.bindingEnabled
  package:fast_rsa/fast_rsa.dart 43:9          RSA._call
  package:fast_rsa/fast_rsa.dart 79:22         RSA._keyPairResponse
  package:fast_rsa/fast_rsa.dart 437:18        RSA.generate
  test/rsa_keys.test.dart 32:37                main.<fn>.<fn>.<fn>
  package:mockito/src/mock.dart 185:45         Mock.noSuchMethod
  test/rsa_keys.test.mocks.dart 75:53          MockRSAKeysProvider.generate
  test/rsa_keys.test.dart 36:21                main.<fn>.<fn>
英文:

I am trying to write some test for an app, but I can't test values stored in a provider. I am relatively new to testing, so there might be something that I am doing wrong, but anyway. What I want to test is to verify is two values are not the same, which should be the expected behavior, but I just can't make it pass.
This is the code that I want to test:

class RSAKeysProvider {
    KeyPair? _keyPair;

    KeyPair? get keyPair =&gt; _keyPair;
    set setKeyPair(KeyPair keyPair) =&gt; _keyPair = keyPair;

    Future&lt;void&gt; generate(String bits) async {
        var keyPair = await RSA.generate(int.parse(bits));
        _keyPair = keyPair;
        notifyListeners();
    }
}

I need to first call the generate() function, which will set the keyPair to actual values, and then check if keyPair.publicKey is different than keyPair.privateKey, but it gives me an error when I try to call generate() with await inside a test.

This is what I have for now, but it doesn't work. The test breaks when it cames to the line "await rsaKeys.generate('2048'). What can I do to make it work? I know the condition is not checking if both are different, but it is just a placeholder, I can't make the code arrive there!

test(&#39;Public and private key should be different&#39;, () async {
      final MockRSAKeysProvider rsaKeys = MockRSAKeysProvider();

      when(rsaKeys.generate(any)).thenAnswer((value) async {
        KeyPair keyPair = await RSA.generate(2048);
        rsaKeys.setKeyPair = keyPair;
      });

      await rsaKeys.generate(&#39;2048&#39;);

      expect(rsaKeys.keyPair?.privateKey, isNotNull);
      expect(rsaKeys.keyPair?.publicKey, isNotNull);
    });

When it arrives at "await rsaKeys.generate('2048'), it gives me this error:

Invalid argument(s): Failed to load dynamic library &#39;librsa_bridge.dylib&#39;: dlopen(librsa_bridge.dylib, 0x0001): tried: &#39;librsa_bridge.dylib&#39; (no such file), &#39;/System/Volumes/Preboot/Cryptexes/OSlibrsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/librsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib&#39; (no such file), &#39;/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/librsa_bridge.dylib&#39; (no such file), &#39;/usr/lib/librsa_bridge.dylib&#39; (no such file, not in dyld cache), &#39;librsa_bridge.dylib&#39; (no such file), &#39;/usr/lib/librsa_bridge.dylib&#39; (no such file, not in dyld cache)
  dart:ffi                                     new DynamicLibrary.open
  package:fast_rsa/bridge/binding.dart 117:33  Binding.openLib
  package:fast_rsa/bridge/binding.dart 26:16   new Binding._internal
  package:fast_rsa/bridge/binding.dart 17:45   Binding._singleton
  package:fast_rsa/bridge/binding.dart         Binding._singleton
  package:fast_rsa/bridge/binding.dart 22:12   new Binding
  package:fast_rsa/fast_rsa.dart 40:32         RSA.bindingEnabled
  package:fast_rsa/fast_rsa.dart               RSA.bindingEnabled
  package:fast_rsa/fast_rsa.dart 43:9          RSA._call
  package:fast_rsa/fast_rsa.dart 79:22         RSA._keyPairResponse
  package:fast_rsa/fast_rsa.dart 437:18        RSA.generate
  test/rsa_keys.test.dart 32:37                main.&lt;fn&gt;.&lt;fn&gt;.&lt;fn&gt;
  package:mockito/src/mock.dart 185:45         Mock.noSuchMethod
  test/rsa_keys.test.mocks.dart 75:53          MockRSAKeysProvider.generate
  test/rsa_keys.test.dart 36:21                main.&lt;fn&gt;.&lt;fn&gt;

答案1

得分: 1

你的存根调用了 RSA.generate,因此它取决于涉及 FFI 并在你运行测试的任何平台上加载动态加载库的 RSA.generate 实现。除非你试图测试 package:fastrsa 本身,你应该避免调用诸如 RSA.generate 这样的东西; 是你应该用存根替换的部分。

Mock 的作用是测试其他代码如何与模拟对象交互。你不能使用 MockRSAKeysProvider 来测试 RSAKeysProvider 本身的行为。如果你想测试 RSAKeysProvider 类的行为,你可以将其更改为接受 RSA.generate 的存根:

class RSAKeysProvider {
  KeyPair? _keyPair;

  KeyPair? get keyPair => _keyPair;
  set setKeyPair(KeyPair keyPair) => _keyPair = keyPair;

  Future<void> generate(String bits, {Future<KeyPair> Function(int)? generate}) async {
    generate ??= RSA.generate;
    var keyPair = await generate(int.parse(bits));
    _keyPair = keyPair;
    notifyListeners();
  }
}

然后在你的测试中:

Future<KeyPair> fakeGenerate(int bits) async {
  return KeyPair('fakePublicKey', 'fakePrivateKey'); // 或使用预先计算的值。
}

test('Public and private key should not be null when generated', () async {
  final RSAKeysProvider rsaKeys = RSAKeysProvider();
  await rsaKeys.generate('2048', generate: fakeGenerate);
  expect(rsaKeys.keyPair?.privateKey, isNotNull);
  expect(rsaKeys.keyPair?.publicKey, isNotNull);
});
英文:

Your stub calls RSA.generate, so it depends on the RSA.generate implementation which involves FFI and loading a dynamically-loaded library on whatever platform you run tests on. Unless you're trying to test package:fastrsa itself, you should avoid calling things such as RSA.generate anyway; that is what you should be replacing with a stub.

The point of a Mock is to test how other code interacts with the mocked object. You cannot use a MockRSAKeysProvider to test the behavior of an RSAKeysProvider itself. If you want to test the behavior of your RSAKeysProvider class, you could change it to accept a stub for RSA.generate:

class RSAKeysProvider {
  KeyPair? _keyPair;

  KeyPair? get keyPair =&gt; _keyPair;
  set setKeyPair(KeyPair keyPair) =&gt; _keyPair = keyPair;

  Future&lt;void&gt; generate(String bits, {Future&lt;KeyPair&gt; Function(int)? generate}) async {
    generate ??= RSA.generate;
    var keyPair = await generate(int.parse(bits));
    _keyPair = keyPair;
    notifyListeners();
  }
}

and then in your test:

Future&lt;KeyPair&gt; fakeGenerate(int bits) async {
  return KeyPair(&#39;fakePublicKey&#39;, &#39;fakePrivateKey&#39;); // Or use precomputed values.
}

test(&#39;Public and private key should not be null when generated&#39;, () async {
  final RSAKeysProvider rsaKeys = RSAKeysProvider();
  await rsaKeys.generate(&#39;2048&#39;, generate: fakeGenerate);
  expect(rsaKeys.keyPair?.privateKey, isNotNull);
  expect(rsaKeys.keyPair?.publicKey, isNotNull);
});

huangapple
  • 本文由 发表于 2023年2月18日 22:11:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493892.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定