update tests

This commit is contained in:
Sergey Penkovsky
2025-05-19 11:14:59 +03:00
parent 50652a14a9
commit e91987c635
2 changed files with 257 additions and 386 deletions

View File

@@ -2,380 +2,91 @@ import 'package:cherrypick/src/binding.dart';
import 'package:test/test.dart';
void main() {
group('Check instance.', () {
group('Without name.', () {
test('Binding resolves null', () {
// --- Instance binding (synchronous) ---
group('Instance Binding (toInstance)', () {
group('Without name', () {
test('Returns null by default', () {
final binding = Binding<int>();
expect(binding.instance, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding = Binding<int>().toInstance(expectedValue);
test('Sets mode to instance', () {
final binding = Binding<int>().toInstance(5);
expect(binding.mode, Mode.instance);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding = Binding<int>().toInstance(expectedValue);
test('isSingleton is true', () {
final binding = Binding<int>().toInstance(5);
expect(binding.isSingleton, true);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>().toInstance(expectedValue);
expect(binding.instance, expectedValue);
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding = Binding<int>().toInstance(expectedValue);
expect(binding.instance, expectedValue);
test('Stores value', () {
final binding = Binding<int>().toInstance(5);
expect(binding.instance, 5);
});
});
group('With name.', () {
test('Binding resolves null', () {
final binding = Binding<int>().withName('expectedValue');
group('With name', () {
test('Returns null by default', () {
final binding = Binding<int>().withName('n');
expect(binding.instance, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
test('Sets mode to instance', () {
final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.mode, Mode.instance);
});
test('Binding check key', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
test('Sets key', () {
final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.key, int);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
test('isSingleton is true', () {
final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.isSingleton, true);
});
test('Binding check value', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
expect(binding.instance, expectedValue);
test('Stores value', () {
final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.instance, 5);
});
test('Binding check value', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
expect(binding.name, 'expectedValue');
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding =
Binding<int>().withName('expectedValue').toInstance(expectedValue);
expect(binding.instance, expectedValue);
});
});
});
group('Check provide.', () {
group('Without name.', () {
test('Binding resolves null', () {
final binding = Binding<int>();
expect(binding.provider, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding = Binding<int>().toProvide(() => expectedValue);
expect(binding.mode, Mode.providerInstance);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding = Binding<int>().toProvide(() => expectedValue);
expect(binding.isSingleton, false);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>().toProvide(() => expectedValue);
expect(binding.provider, expectedValue);
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding = Binding<int>().toProvide(() => expectedValue);
expect(binding.provider, expectedValue);
test('Sets name', () {
final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.name, 'n');
});
});
group('With name.', () {
test('Binding resolves null', () {
final binding = Binding<int>().withName('expectedValue');
expect(binding.provider, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.mode, Mode.providerInstance);
});
test('Binding check key', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.key, int);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.isSingleton, false);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.provider, expectedValue);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.name, 'expectedValue');
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue);
expect(binding.provider, expectedValue);
});
test('Multiple toInstance calls change value', () {
final binding = Binding<int>().toInstance(1).toInstance(2);
expect(binding.instance, 2);
});
});
group('Check Async provider.', () {
test('Binding resolves value asynchronously', () async {
final expectedValue = 5;
final binding = Binding<int>().toProvideAsync(() async => expectedValue);
final result = await binding.asyncProvider?.call();
expect(result, expectedValue);
// --- Instance binding (asynchronous) ---
group('Async Instance Binding (toInstanceAsync)', () {
test('Resolves instanceAsync with expected value', () async {
final binding = Binding<int>().toInstanceAsync(Future.value(42));
expect(await binding.instanceAsync, 42);
});
test('Binding resolves value asynchronously with params', () async {
final expectedValue = 5;
final binding = Binding<int>().toProvideAsyncWithParams(
(param) async => expectedValue + (param as int));
final result = await binding.asyncProviderWithParams?.call(3);
expect(result, expectedValue + 3);
});
});
group('Check singleton provide.', () {
group('Without name.', () {
test('Binding resolves null', () {
final binding = Binding<int>().singleton();
expect(binding.provider, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding =
Binding<int>().toProvide(() => expectedValue).singleton();
expect(binding.mode, Mode.providerInstance);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding =
Binding<int>().toProvide(() => expectedValue).singleton();
expect(binding.isSingleton, true);
});
test('Binding check value', () {
final expectedValue = 5;
final binding =
Binding<int>().toProvide(() => expectedValue).singleton();
expect(binding.provider, expectedValue);
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding =
Binding<int>().toProvide(() => expectedValue).singleton();
expect(binding.provider, expectedValue);
});
});
group('With name.', () {
test('Binding resolves null', () {
final binding = Binding<int>().withName('expectedValue').singleton();
expect(binding.provider, null);
});
test('Binding check mode', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.mode, Mode.providerInstance);
});
test('Binding check key', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.key, int);
});
test('Binding check singleton', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.isSingleton, true);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.provider, expectedValue);
});
test('Binding check value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.name, 'expectedValue');
});
test('Binding resolves value', () {
final expectedValue = 5;
final binding = Binding<int>()
.withName('expectedValue')
.toProvide(() => expectedValue)
.singleton();
expect(binding.provider, expectedValue);
});
});
});
test('Binding returns null providerWithParams if not set', () {
final binding = Binding<int>();
expect(binding.providerWithParams(123), null);
});
test('Binding withName changes isNamed to true', () {
final binding = Binding<int>().withName('foo');
expect(binding.isNamed, true);
expect(binding.name, 'foo');
});
// Проверка singleton provider вызывается один раз
test('Singleton provider only called once', () {
int counter = 0;
final binding = Binding<int>().toProvide(() {
counter++;
return counter;
}).singleton();
final first = binding.provider;
final second = binding.provider;
expect(first, equals(second));
expect(counter, 1);
});
// Повторный вызов toInstance влияет на значение
test('Multiple toInstance calls changes instance', () {
final binding = Binding<int>().toInstance(1).toInstance(2);
expect(binding.instance, 2);
});
// Проверка mode после chaining
test('Chained withName and singleton preserves mode', () {
final binding =
Binding<int>().toProvide(() => 3).withName("named").singleton();
expect(binding.mode, Mode.providerInstance);
});
group('Check toInstanceAsync.', () {
test('Binding resolves instanceAsync with expected value', () async {
final expectedValue = 42;
final binding =
Binding<int>().toInstanceAsync(Future.value(expectedValue));
final result = await binding.instanceAsync;
expect(result, equals(expectedValue));
});
test('Binding instanceAsync does not affect instance', () {
test('Does not affect instance', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.instance, null);
});
test('Binding mode is set to instance', () {
test('Sets mode to instance', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.mode, Mode.instance);
});
test('Binding isSingleton is true after toInstanceAsync', () {
test('isSingleton is true after toInstanceAsync', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.isSingleton, isTrue);
});
test('Binding withName combines with toInstanceAsync', () async {
test('Composes with withName', () async {
final binding = Binding<int>()
.withName('asyncValue')
.toInstanceAsync(Future.value(7));
@@ -384,11 +95,177 @@ void main() {
expect(await binding.instanceAsync, 7);
});
test('Binding instanceAsync keeps value after multiple awaits', () async {
test('Keeps value after multiple awaits', () async {
final binding = Binding<int>().toInstanceAsync(Future.value(123));
final result1 = await binding.instanceAsync;
final result2 = await binding.instanceAsync;
expect(result1, equals(result2));
});
});
// --- Provider binding (synchronous) ---
group('Provider Binding (toProvide)', () {
group('Without name', () {
test('Returns null by default', () {
final binding = Binding<int>();
expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5);
expect(binding.mode, Mode.providerInstance);
});
test('isSingleton is false by default', () {
final binding = Binding<int>().toProvide(() => 5);
expect(binding.isSingleton, false);
});
test('Returns provided value', () {
final binding = Binding<int>().toProvide(() => 5);
expect(binding.provider, 5);
});
});
group('With name', () {
test('Returns null by default', () {
final binding = Binding<int>().withName('n');
expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.mode, Mode.providerInstance);
});
test('Sets key', () {
final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.key, int);
});
test('isSingleton is false by default', () {
final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.isSingleton, false);
});
test('Returns provided value', () {
final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.provider, 5);
});
test('Sets name', () {
final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.name, 'n');
});
});
});
// --- Async provider binding ---
group('Async Provider Binding', () {
test('Resolves asyncProvider value', () async {
final binding = Binding<int>().toProvideAsync(() async => 5);
expect(await binding.asyncProvider?.call(), 5);
});
test('Resolves asyncProviderWithParams value', () async {
final binding = Binding<int>()
.toProvideAsyncWithParams((param) async => 5 + (param as int));
expect(await binding.asyncProviderWithParams?.call(3), 8);
});
});
// --- Singleton provider binding ---
group('Singleton Provider Binding', () {
group('Without name', () {
test('Returns null if no provider set', () {
final binding = Binding<int>().singleton();
expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
});
test('isSingleton is true', () {
final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.isSingleton, true);
});
test('Returns singleton value', () {
final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.provider, 5);
});
test('Returns same value each call and provider only called once', () {
int counter = 0;
final binding = Binding<int>().toProvide(() {
counter++;
return counter;
}).singleton();
final first = binding.provider;
final second = binding.provider;
expect(first, equals(second));
expect(counter, 1);
});
});
group('With name', () {
test('Returns null if no provider set', () {
final binding = Binding<int>().withName('n').singleton();
expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
});
test('Sets key', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.key, int);
});
test('isSingleton is true', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.isSingleton, true);
});
test('Returns singleton value', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.provider, 5);
});
test('Sets name', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.name, 'n');
});
});
test('Chained withName and singleton preserves mode', () {
final binding =
Binding<int>().toProvide(() => 3).withName("named").singleton();
expect(binding.mode, Mode.providerInstance);
});
});
// --- WithName / Named binding, isNamed, edge-cases ---
group('Named binding & helpers', () {
test('withName sets isNamed true and stores name', () {
final binding = Binding<int>().withName('foo');
expect(binding.isNamed, true);
expect(binding.name, 'foo');
});
test('providerWithParams returns null if not set', () {
final binding = Binding<int>();
expect(binding.providerWithParams(123), null);
});
});
}

View File

@@ -3,46 +3,44 @@ import 'package:cherrypick/src/scope.dart';
import 'package:test/test.dart';
void main() {
group('Without parent scope.', () {
test('Parent scope is null.', () {
// --------------------------------------------------------------------------
group('Scope & Subscope Management', () {
test('Scope has no parent if constructed with null', () {
final scope = Scope(null);
expect(scope.parentScope, null);
});
test('Open sub scope.', () {
test('Can open and retrieve the same subScope by key', () {
final scope = Scope(null);
final subScope = scope.openSubScope('subScope');
expect(scope.openSubScope('subScope'), subScope);
});
test("Container throws state error if the value can't be resolved", () {
test('closeSubScope removes subscope so next openSubScope returns new', () {
final scope = Scope(null);
final subScope = scope.openSubScope("child");
expect(scope.openSubScope("child"), same(subScope));
scope.closeSubScope("child");
final newSubScope = scope.openSubScope("child");
expect(newSubScope, isNot(same(subScope)));
});
});
// --------------------------------------------------------------------------
group('Dependency Resolution (standard)', () {
test("Throws StateError if value can't be resolved", () {
final scope = Scope(null);
expect(() => scope.resolve<String>(), throwsA(isA<StateError>()));
});
test('Container resolves value after adding a dependency', () {
test('Resolves value after adding a dependency', () {
final expectedValue = 'test string';
final scope = Scope(null)
.installModules([TestModule<String>(value: expectedValue)]);
expect(scope.resolve<String>(), expectedValue);
});
});
group('With parent scope.', () {
/*
test(
"Container bind() throws state error (if it's parent already has a resolver)",
() {
final parentScope = new Scope(null)
.installModules([TestModule<String>(value: "string one")]);
final scope = new Scope(parentScope);
expect(
() => scope.installModules([TestModule<String>(value: "string two")]),
throwsA(isA<StateError>()));
});
*/
test('Container resolve() returns a value from parent container.', () {
test('Returns a value from parent scope', () {
final expectedValue = 5;
final parentScope = Scope(null);
final scope = Scope(parentScope);
@@ -52,8 +50,7 @@ void main() {
expect(scope.resolve<int>(), expectedValue);
});
test('Container resolve() returns a several value from parent container.',
() {
test('Returns several values from parent container', () {
final expectedIntValue = 5;
final expectedStringValue = 'Hello world';
final parentScope = Scope(null).installModules([
@@ -66,15 +63,22 @@ void main() {
expect(scope.resolve<String>(), expectedStringValue);
});
test("Container resolve() throws a state error if parent hasn't value too.",
() {
test("Throws StateError if parent hasn't value too", () {
final parentScope = Scope(null);
final scope = Scope(parentScope);
expect(() => scope.resolve<int>(), throwsA(isA<StateError>()));
});
test("After dropModules resolves fail", () {
final scope = Scope(null)..installModules([TestModule<int>(value: 5)]);
expect(scope.resolve<int>(), 5);
scope.dropModules();
expect(() => scope.resolve<int>(), throwsA(isA<StateError>()));
});
});
group('Named dependencies', () {
// --------------------------------------------------------------------------
group('Named Dependencies', () {
test('Resolve named binding', () {
final scope = Scope(null)
..installModules([
@@ -103,7 +107,8 @@ void main() {
});
});
group('Provider with params', () {
// --------------------------------------------------------------------------
group('Provider with parameters', () {
test('Resolve dependency using providerWithParams', () {
final scope = Scope(null)
..installModules([
@@ -112,14 +117,12 @@ void main() {
}),
]);
expect(scope.resolve<int>(params: 3), 6);
expect(
() => scope.resolve<int>(),
throwsA(isA<StateError>()),
);
expect(() => scope.resolve<int>(), throwsA(isA<StateError>()));
});
});
group('Async resolution', () {
// --------------------------------------------------------------------------
group('Async Resolution', () {
test('Resolve async instance', () async {
final scope = Scope(null)
..installModules([
@@ -148,10 +151,7 @@ void main() {
}),
]);
expect(await scope.resolveAsync<int>(params: 2), 6);
expect(
() => scope.resolveAsync<int>(),
throwsA(isA<StateError>()),
);
expect(() => scope.resolveAsync<int>(), throwsA(isA<StateError>()));
});
test('tryResolveAsync returns null for missing', () async {
@@ -161,34 +161,28 @@ void main() {
});
});
group("Drop modules", () {
test("After dropModules resolves fail", () {
final scope = Scope(null)..installModules([TestModule<int>(value: 5)]);
expect(scope.resolve<int>(), 5);
scope.dropModules();
expect(() => scope.resolve<int>(), throwsA(isA<StateError>()));
});
});
group("Subscope closing", () {
test("closeSubScope removes subscope", () {
final scope = Scope(null);
final subScope = scope.openSubScope("child");
expect(scope.openSubScope("child"), same(subScope));
scope.closeSubScope("child");
final newSubScope = scope.openSubScope("child");
expect(newSubScope, isNot(same(subScope))); // New instance after close
});
});
group("tryResolve returns null if not found", () {
test("Returns null for missing dependency", () {
// --------------------------------------------------------------------------
group('Optional resolution and error handling', () {
test("tryResolve returns null for missing dependency", () {
final scope = Scope(null);
expect(scope.tryResolve<int>(), isNull);
});
// Не реализован:
// test("Container bind() throws state error (if it's parent already has a resolver)", () {
// final parentScope = new Scope(null).installModules([TestModule<String>(value: "string one")]);
// final scope = new Scope(parentScope);
// expect(
// () => scope.installModules([TestModule<String>(value: "string two")]),
// throwsA(isA<StateError>()));
// });
});
}
// ----------------------------------------------------------------------------
// Вспомогательные модули
class TestModule<T> extends Module {
final T value;
final String? name;