英文:
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 => _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 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('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), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/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>
答案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 => _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();
}
}
and then in your test:
Future<KeyPair> fakeGenerate(int bits) async {
return KeyPair('fakePublicKey', 'fakePrivateKey'); // Or use precomputed values.
}
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);
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论