GetIt is Service Locator for Dart and Flutter projects (https://github.com/fluttercommunity/get_it)
I think GetIt should give us some details about circular dependency.
When I run my tests for example there is an infinite loop and they never end.
The get_it is for defining global objects and services, and for providing a locator functionality with which to access them. If you have a circular dependency among your global services, there's a good chance that you have designed those services to be tightly coupled with each other. For example:
class ServiceA {
final ServiceB b;
ServiceA(this.b);
void foo() {
b.bar();
}
void bar() { ... }
}
class ServiceB {
final ServiceA a;
ServiceB(this.a);
void foo() {
a.bar();
}
void bar() { ... }
}
// GetIt Initialization
GetIt g = GetIt.instance;
g.registerLazySingleton<ServiceA>(() => ServiceA(g.get<ServiceB>()));
g.registerLazySingleton<ServiceB>(() => ServiceB(g.get<ServiceA>()));
After this, calling GetIt.I.get targeting either ServiceA or ServiceB could create the infinite loop caused by circular dependency. With this implementation, there's no real way to prevent the loop without going down a rabbit hole of checks and deferred execution.
The real solution to circular dependency is often simple: design your app to not have it in the first place. For example, in the above example, ServiceA has a rigid dependency on ServiceB in the constructor and vice versa. Instead, the services could use a service locator to instead fetch the reference to the necessary service during execution of the method that needs it. To put that another way, we already are using get_it, so why not use it here?
class ServiceA {
ServiceA();
void foo() {
final b = GetIt.I.get<ServiceB>();
b.bar();
}
void bar() { ... }
}
class ServiceB {
ServiceB();
void foo() {
final a = GetIt.I.get<ServiceA>();
a.bar();
}
}
// GetIt Initialization
GetIt g = GetIt.instance;
g.registerLazySingleton<ServiceA>(() => ServiceA());
g.registerLazySingleton<ServiceB>(() => ServiceB());
Now the dependencies that ServiceA and ServiceB have on each other is much more dynamic and is not tied to their respective constructors. There is no longer a circular dependency problem, and the get_it initializer will have no problem initializing the singletons when they are accessed.
To solve this problem. Here a part solution with this example:
Some interfaces and implementations:
abstract class SuperLetter {}
abstract class A implements SuperLetter {}
class AC implements A {
final B b;
AC({this.b});
#override
String toString() {
if (b == null) {
return "Im an A.";
}
return "Im an A and my B is: $b";
}
}
abstract class B implements SuperLetter {}
class BC implements B {
final C c;
BC({this.c});
#override
String toString() {
if (c == null) {
return "Im a B.";
}
return "Im a B and my C: $c";
}
}
abstract class C implements SuperLetter {}
class CC implements C {
final SuperLetter a;
CC({this.a});
#override
String toString() {
if (a == null) {
return "Im a C.";
}
return "Im a C and my A: $a"; // might never be reached because of circular dependency
}
}
A new Exception to throw when circular dependency is detected.
class CircularDependencyException implements Exception {
final wantedType;
final calledTypes;
CircularDependencyException({
this.wantedType,
this.calledTypes,
});
#override
String toString() {
String message = '🔺 Circular dependency ';
for (final e in calledTypes) {
message += '${e.toString()}';
if (e == calledTypes.last) {
message += ' -> $wantedType';
} else {
message += ' -> ';
}
}
return message;
}
}
To fight against circular dependency:
class GetItProtector {
var _wantedType;
Set _calledTypes;
final GetIt getIt;
GetItProtector(this.getIt);
_check<T>() {
if (_wantedType == null) {
_wantedType = T;
_calledTypes = Set()..add(T);
} else {
if (_calledTypes.contains(T)) {
throw CircularDependencyException(wantedType: T, calledTypes: _calledTypes);
}
_calledTypes.add(T);
}
}
T call<T>() {
_check<T>();
final instance = getIt<T>();
if (instance.runtimeType == _wantedType) {
_wantedType = null;
_calledTypes.clear();
}
return instance;
}
}
Example with A -> B -> C -> A circular dependency:
void main() {
GetIt g = GetIt.instance;
final myGet = GetItProtector(g);
g.registerLazySingleton<A>(() => AC(
b: myGet(),
));
g.registerLazySingleton<B>(() => BC(
c: myGet(),
));
g.registerLazySingleton<C>(() => CC(
a: myGet<A>(),
));
A a = myGet();
print(a);
}
Output:
Error while creating C
Stack trace: ......
Error while creating B
Stack trace: ......
Error while creating A
Stack trace: ......
Unhandled exception:
🔺 Circular dependency A -> B -> C -> A
....
Example with A -> B -> C -> B
void main() {
GetIt g = GetIt.instance;
final myGet = GetItProtector(g);
g.registerLazySingleton<A>(() => AC(
b: myGet(),
));
g.registerLazySingleton<B>(() => BC(
c: myGet(),
));
g.registerLazySingleton<C>(() => CC(
a: myGet<B>(),
));
A a = myGet();
print(a);
}
Output
Error while creating C
Stack trace: ......
Error while creating B
Stack trace: ......
Error while creating A
Stack trace: ......
Unhandled exception:
🔺 Circular dependency A -> B -> C -> B
....
In this solution I don't test if type is a real type and not Object or dynamic.
Related
Description:
I have already tested methodA() and methodB() so I can be sure that they are covered.
What are the ways to test methodToBeTested() by mocking methodA() and methodB() that are in the same file? The parameters are passed through the methodToBeTested() to the methodA() and methodB() to properly test these methods using injection.
Note: They are cannot be extracted to a different class since it is a related logic of the calculation service and these methods are already atomically is separated.
Code:
class ClassForTesting {
int methodToBeTested(String a, String b) {
// Calculation in this method also is a bit more difficult
return methodA() + methodB();
}
int methodA(String a) {
int value = 1;
// Here is calculation logic that has been tested
return value;
}
int methodB(String b) {
int value = 2;
// Here is calculation logic that has been tested
return value;
}
}
What has been done:
I have tried several approaches from Mockito, but it doesn't allow to do such a trick:
#GenerateMocks - is creating a mock and requires me to stub each method using when(), even methodToBeTested().
By extending Fake using the next construction:
class Mock extends Fake implements PasswordValidatorService {}
But in this way, I'm only inheriting the PasswordValidatorService's behavior instead of instead implementation and each non-overridden method throws UnimplementedError. Thus, I'm not able to override methodToBeTested() and call its super implementation.
I found that Mockito for Java has #Spy construction that would be perfect in this case but unfortunately it is not available for Dart and Flutter.
The only way I currently came is to create my own Mock:
class MockClassForTesting extends ClassForTesting {
#override
int methodA() {
return 2;
}
#override
int methodB() {
return 5;
}
}
But this implementation doesn't allow me to use Mockito's flexibility of when() construction since I must have different methodA() and methodB() returns.
This fact forces me to have additional variables in my MockClassForTesting to achieve when() construction functionality.
The questions:
What would be the best way to achieve my purposes?
Can be the same mocking approach to be used during the Widget testing?
One approach would be to use a hybrid approach where you create your own derived class but where some of its overrides delegate to a Mock implementation. For example:
class ClassForTesting {
int methodToBeTested(String a, String b) {
// Calculation in this method also is a bit more difficult
return methodA(a) + methodB(b);
}
int methodA(String a) {
int value = 1;
// Here is calculation logic that has been tested
return value;
}
int methodB(String b) {
int value = 2;
// Here is calculation logic that has been tested
return value;
}
}
class PartialMockClassForTesting extends ClassForTesting {
final mock = MockClassForTesting();
#override
int methodA(String a) => mock.methodA(a);
#override
int methodB(String b) => mock.methodB(b);
}
#GenerateMocks([ClassForTesting])
void main() {
test('Test partial mock', () {
var partialMock = PartialMockClassForTesting();
when(partialMock.methodA('hello')).thenReturn(42);
when(partialMock.methodA('goodbye')).thenReturn(-42);
when(partialMock.methodB('world')).thenReturn(10);
expect(partialMock.methodToBeTested('hello', 'world'), 52);
expect(partialMock.methodToBeTested('goodbye', 'world'), -32);
});
}
If you want to conditionally mock certain methods, you could have your overrides check boolean flags to conditionally call either the mock or the real implementation. For example:
class PartialMockClassForTesting extends ClassForTesting {
final mock = MockClassForTesting();
final shouldMock = <Function, bool>{};
#override
int methodA(String a) =>
shouldMock[methodA] ?? false ? mock.methodA(a) : super.methodA(a);
#override
int methodB(String b) =>
shouldMock[methodB] ?? false ? mock.methodB(b) : super.methodB(b);
}
#GenerateMocks([ClassForTesting])
void main() {
test('Test partial mock', () {
var partialMock = PartialMockClassForTesting();
partialMock.shouldMock[partialMock.methodA] = true;
partialMock.shouldMock[partialMock.methodB] = true;
...
Currently making a large app with Flutter and I m stuck on the architecture of service class. There is a service class for the firestore CRUD operations.This class has many methods and I want split it into small pieces. I use an abstract class to protect methods.I find a way with mixins but don't know it's a good one or not.
https://gist.github.com/pMertDogan/fcd301d768f3980a898cec33a9acaa4f.
//Extend CRUDSERVICE rules aka abstract class => Test
mixin Update{
void updateSomething();
}
mixin Read{
void readSomething();
}
//BASE class for CRUDSERVICE
abstract class Test with Update,Read{
doSomeCreateOP(String x);
}
//
class CrudService extends Test with UpdateService , ReadService{
#override
doSomeCreateOP(String x) {
print('crated ' + x);
}
}
mixin UpdateService{
// #override
void updateSomething() {
print('updated');
}
}
mixin ReadService{
// #override
void readSomething() {
print('read');
}
}
void main() {
CrudService croudService = CrudService();
croudService.doSomeCreateOP(' dartSide');
croudService.updateSomething();
croudService.readSomething();
}
CreateService and UpdateService mixins are just sample.I am thinking like if I need update user information all methods are handed by UserServiceMix mixin if it's Friend then its hanled by FriendServiceMix so I can split them like a domain-based.Each mixin is responsible for specific operations.I can manage then on independent files and summary of them with the help of mixin.
Is it good way to go?
I believe it is a good way to go. It is a quite flexible approach. We use it for API versioning as well.
abstract class Service {
void method1();
void method2();
}
mixin Method1V1 {
void method1() {
print("method1");
}
}
mixin Method2V1 {
void method2() {
print("method2");
}
}
mixin Method2V2 {
void method2() {
print("method2 with changed logic");
}
}
class ServiceV1 extends Service with Method1V1, Method2V1 {
}
class ServiceV2 extends Service with Method1V1, Method2V2 {
}
void main() {
final version = 2;
final Service service = version == 1 ? ServiceV1() : ServiceV2();
service.method2();
}
Is there way to overriding method in Dart like JAVA, for example:
public class A {
public void handleLoad() {
}
}
And when overriding:
A a = new A() {
#Override
public void handleLoad() {
// do some code
}
};
No, Dart does not have anonymous classes. You have to create a class that extends A and instantiate it.
No but it much less useful in Dart because you can just reassign function:
typedef void PrintMsg(msg);
class Printer {
PrintMsg foo = (m) => print(m);
}
main() {
Printer p = new Printer()
..foo('Hello') // Hello
..foo = ((String msg) => print(msg.toUpperCase()))
..foo('Hello'); //HELLO
}
However you will need some extra boilerplate to access instance.
Use type Function:
class A {
final Function h
A(this.h);
void handleLoad(String loadResult) { h(loadResult); }
}
Or
class A {
final Function handleLoad;
A(this.handleLoad);
}
A a = new A((String loadResult){
//do smth.
});
I want to test the Event to check each condition and I need to mock the repository variable from the abstract class
This is the abstract class for AuthorizationEvent:
#immutable
abstract class AuthorizationEvent {
final repository = AuthorizationRepository();
Stream<AuthorizationState> applyAsync({AuthorizationState currentState, AuthorizationBloc bloc});
}
This is the Event:
class LoadAuthorizationEvent extends AuthorizationEvent {
#override
Stream<AuthorizationState> applyAsync({AuthorizationState currentState, AuthorizationBloc bloc}) async* {
try {
repository.user?.reload();
if (repository.user != null && !repository.user.isAnonymous) {
if (AppConfig.useEmailVerification) {
if (repository.user.emailVerified) {
yield InAuthorizationState(repository.user);
} else {
yield EmailVerificationAuthState(repository.user.email);
}
} else {
yield InAuthorizationState(repository.user);
}
} else {
yield const OutAuthorizationState();
}
} catch (_, stackTrace) {
yield AuthorizationError.handle(_, currentState, stackTrace: stackTrace);
}
}
}
This is an old question from the days I didn't know too much about flutter unit testing. The answer to that you need to pass the repository as a parameter and there's an interesting approach for this:
abstract class AuthorizationEvent {
final AuthorizationRepository _repository;
AuthorizationEvent(AuthorizationRepository? repository)
: _repository = repository ?? AuthorizationRepository();
Stream<AuthorizationState> applyAsync({AuthorizationState currentState, AuthorizationBloc bloc});
}
As you may see you could leave this parameter in null and and instance will be created anyways so your code will still remains the same. Now for unit tests you should mock the repository and pass it through.
A data class in Dart:
import 'package:validate/validate.dart';
class AuthUser {
final String email, token, username, bio, image;
AuthUser(this.email, this.token, this.username, this.bio, this.image) {
Validate.isEmail(this.email);
}
#override
String toString() {
return 'AuthUser{email: $email, token: $token, username: $username, bio: $bio, image: $image}';
}
}
where Validate.isEmail will throws an Error when failed to match:
static void matchesPattern(String input, RegExp pattern,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
if (pattern.hasMatch(input) == false) {
throw new ArgumentError(message);
}
}
static void isEmail(String input,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
matchesPattern(input,new RegExp(PATTERN_EMAIL),message);
}
Now I want to use an elegant way to new this class.
When using Scala, I can use Try(new AuthUser(...)) and patten-matching it.
And in Dart, first I tried RxDart,
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
Observable.just(AuthUser("email", "token", "username", "bio", "img"))
.doOnError((e, s) => print("oh no"))
.listen((e) => print(e.toString()));
});
}
Not work, the test failed for the error(which means RxDart doesn't catch errors at all!!!)
And I want to try Future, failed also.
And I want to use dartz, but I am worried because there is just one maintainer...
Any advice?
If you are OK with using Future what's wrong with this advice: Using Future.sync() to wrap your code? The code will look like this:
void main() {
var f = Future.sync(() {AuthUser("email", "token", "username", "bio", "img"); });
f.then((v) => print("Value: " + v.toString())).catchError((e) => print("Failure: " +e.toString()));
}
The main trick is that Future.sync effectively enables lazy evaluation of the parameter but you have to pass your parameter wrapped in a function. This is actually the same trick Scala compiler does for Try (i.e. for call-by-name parameters) but takes adding a few brackets around.
If you only want the basic functionality of returning either type based on whether an exception occurred or not then you can easily create a utility class such as below.
Otherwise I recommend #SergGr's answer about using Future.sync since it gives you more monadic like pipeline.
void main() {
Try<Error, void> result = Try.it(() => Validate.isEmail("test-example.com"));
if (result is Success) {
print("Good");
} else if (result is Failure) {
print("Error: " + result.exception().toString());
}
}
typedef TryExec<V> = V Function();
abstract class Try<E extends Error, V> {
static Try<E, V> it<E extends Error, V>(TryExec<V> fn) {
try {
return Try.success(fn());
} catch (e) {
return Try.failure(e);
}
}
static Try<E, V> failure<E extends Error, V>(Error e) {
return new Failure(e);
}
static Try<E, V> success<E extends Error, V>(V v) {
return new Success(v);
}
}
class Failure<E extends Error, V> extends Try<E, V> {
final E _e;
Failure(this._e);
E exception() => _e;
}
class Success<E extends Error, V> extends Try<E, V> {
final V _v;
Success(this._v);
V value() => _v;
}