Testing
Test code that calls Crypt without a device, a keychain or a mocking library.
On this page
Config-Key Mode#
Bind an encrypter under the encrypter key, the one EncryptionServiceProvider binds in production, and Crypt uses it. Every setUp() resets the container, as every Magic test does.
import 'package:flutter_test/flutter_test.dart';
import 'package:magic/magic.dart';
import 'package:magic_encryption/magic_encryption.dart';
void main() {
setUp(() {
MagicApp.reset();
Magic.flush();
Magic.singleton(
'encrypter',
() => MagicEncrypter('0123456789abcdef0123456789abcdef'),
);
});
test('a value survives a round trip', () {
expect(Crypt.decrypt(Crypt.encrypt('hello')), 'hello');
});
test('a malformed payload is refused', () {
expect(
() => Crypt.decrypt('not-a-payload'),
throwsA(isA<MagicDecryptException>()),
);
});
}
To test the provider itself, boot Magic with 'app': {'key': '...', 'providers': [(app) => EncryptionServiceProvider(app)]}; Magic.init reads path_provider, so mock its channel in setUpAll.
Do not assert that a payload from another key throws. About one wrong-key payload in 256 unpads cleanly and returns garbage, so assert instead that it never yields the plaintext.
Device-Key Mode#
Swap the vault for core's fake with Vault.fake(). Crypt caches the device encrypter in a static field that Magic.flush() does not reset, so a key from an earlier test would outlive its fake vault: clear it in tearDown.
import 'package:flutter_test/flutter_test.dart';
import 'package:magic/magic.dart';
import 'package:magic_encryption/magic_encryption.dart';
void main() {
setUp(() {
MagicApp.reset();
Magic.flush();
Vault.fake();
});
tearDown(() async {
await Crypt.clearDeviceKey();
});
test('the device key is created on first use', () async {
expect(await Crypt.hasDeviceKey(), isFalse);
final String secret = await Crypt.encryptWithDeviceKey('token');
expect(await Crypt.hasDeviceKey(), isTrue);
expect(await Crypt.decryptWithDeviceKey(secret), 'token');
});
}
Pinning the IV#
MagicEncrypter.encrypt takes an iv: of 16 bytes so a test can assert an exact payload. Leave it unset in production: a reused IV leaks equality of plaintext prefixes.
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:magic_encryption/magic_encryption.dart';
void main() {
test('the same key, value and IV give the same payload', () {
final MagicEncrypter encrypter = MagicEncrypter('0123456789abcdef0123456789abcdef');
final Uint8List iv = Uint8List(16);
final String payload = encrypter.encrypt('hello', iv: iv);
expect(payload.split(':').first, base64.encode(iv));
expect(encrypter.encrypt('hello', iv: iv), payload);
});
}
Related Documentation#
- Crypt: the facade under test.
- Device Key: the cached encrypter and the vault.
- Wire Format: what a payload contains.