Reputation: 782
In my production code I set environment variables using the --DART-DEFINE
flag and then retrieve them by using String.fromEnvironment
.
How can I override the value returned by String.fromEnvironment
in my unit tests, to test how my production code responds to different environment variable values?
Upvotes: 8
Views: 3668
Reputation: 2450
I personally would recommend this approach:
class EnvironmentVariables {
String get flutterAppFlavor =>
const String.fromEnvironment('FLUTTER_APP_FLAVOR');
}
This builds on what @jamieastley said, but is very lightweight, and in your tests you can easily override it like this:
class EnvironmentVariablesMock implements EnvironmentVariables {
String _flutterAppFlavor = "";
@override
String get flutterAppFlavor => _flutterAppFlavor;
set flutterAppFlavor(String flutterAppFlavor) =>
_flutterAppFlavor = flutterAppFlavor;
}
As with the approach of @jamieastley, the idea here would be to implement it into you classes like this:
class MyClass{
final _environmentVariables = getIt.get<EnvironmentVariables>();
SomeObject doSomething(){
var flavor = _environmentVariables.flutterAppFlavor;
[...]
}
}
Then you can simply overwrite the environment variables as desired in your tests like this:
void main() {
late EnvironmentVariablesMock environmentVariablesMock;
late MyClass myClass;
setUp(() {
getIt.reset();
environmentVariablesMock = EnvironmentVariablesMock();
getIt.registerSingleton<EnvironmentVariables>(environmentVariablesMock);
myClass= MyClass ();
});
test(
'doSomething should do something based on flavor',
() async {
environmentVariablesMock.flutterAppFlavor = "staging";
SomeObject response = myClass.doSomething;
expect(response , equals(SomeObject("The staging value"));
},
);
}
Upvotes: 0
Reputation: 610
Wrap your environment variables in a class and use your preferred dependency injection or service locator package (eg. get_it) to access your injected class from within your app.
Your environment_config.dart
:
class EnvironmentConfig {
final String someValue;
EnvironmentConfig({
required this.someValue,
});
factory EnvironmentConfig.init() {
const someValue = String.fromEnvironment('someEnvVar');
return EnvironmentConfig(someValue: someValue);
}
}
main.dart
void main() {
// instance can now be accessed from any class/widget within your app
GetIt.I.registerSingleton<EnvironmentConfig>(EnvironmentConfig.init());
runApp(MyApp());
}
Then within unit tests, you can inject your own instance of EnvironmentConfig
with your own values:
void main() {
setUp(() {
final config = EnvironmentConfig(someValue: 'myCustomValue');
GetIt.I.registerSingleton<EnvironmentConfig>(config);
});
tearDown(() {
GetIt.I.reset();
});
test('Verify env vars', () {
expect(GetIt.I<EnvironmentConfig>().someValue, equals('myCustomValue');
});
}
EDIT: Given this is now the top result when searching for an answer to this, it's worth noting that the trade-off to the above approach is that it doesn't cater for tree-shaking, as the EnvironmentConfig
class is not a compile-time constant when using the factory constructor.
Upvotes: 5
Reputation: 152
There are two good answers here, but they do miss the point.
The point is:
The Question:
How can I override the value returned by String.fromEnvironment in my unit tests
The onpoint, correct answer is:
You cannot do that.
You have to modify and complicate your code to make it testable. The data structure that holds environmental data in Dart is read only, so it is impossible to do what you require in this language.
Upvotes: 0
Reputation: 965
The flutter test
command supports --dart-define=NAME=VALUE
, but this is annoying to automate. It's also not possible to change dart defines at runtime.
What my team does is declare all of the dart defines in a single file, and use separate global variables to override them during tests:
String? fooDefineOverride;
String get fooDefine => fooDefineOverride ?? const String.fromEnvironment('foo');
void resetOverrides() {
fooDefineOverride = null;
}
void main() {
setUpAll(() {
resetOverrides();
});
test('Foo is foobar', () {
fooDefineOverride = 'foobar';
// ... do tests
});
}
Upvotes: 3