passsy / avoid-singletonsr3
Avoid Singletons
PublicInject dependencies instead of reaching for a singleton, so tests can replace them with a fake or a mock.
tests, run by the registry7 passed · slowest 4.6 ms
✓fires4.6 ms
a static instance with a private constructor fires
write lib/app_database.dartfired: line 3, line 5
/// The one database connection.
class AppDatabase {
static final AppDatabase instance = AppDatabase._internal();
AppDatabase._internal();
int queries = 0;
}✓fires2.2 ms
a nullable _instance field fires
write lib/cache.dartfired: line 2
class Cache {
static Cache? _instance;
}✓quiet2.2 ms
a singleton that was already there stays quiet
write lib/app_database.dart · wrote line 7stayed quiet
/// The one database connection.
class AppDatabase {
static final AppDatabase instance = AppDatabase._internal();
AppDatabase._internal();
int queries = 0;
}✓quiet2.2 ms
static const configuration stays quiet
write lib/retry_policy.dartstayed quiet
class RetryPolicy {
static const int maxAttempts = 3;
}✓quiet2.3 ms
a field merely containing instance in its name stays quiet
write lib/client_registry.dartstayed quiet
class ClientRegistry {
static final int instanceCount = 0;
}✓fires3.2 ms
points at the principle when the user asks for a singleton in a Dart project
prompt · in fixtures/dart_appfired: "singleton"
Make the API client a singleton✓quiet2.1 ms
stays quiet on the same words outside a Dart project
promptstayed quiet
Make the API client a singletondetector.test.tseach detect() call runs the real detectorEdit
import { expect, test } from "vitest";
import { detect, edit, prompt, source, write } from "@wellactually/sdk/v3/test";
const database = source`
/// The one database connection.
class AppDatabase {
static final AppDatabase instance = AppDatabase._internal();
AppDatabase._internal();
int queries = 0;
}
`;
test("a static instance with a private constructor fires", () => {
expect(detect(write("lib/app_database.dart", database))).toEqual([
{ line: 3 },
{ line: 5 },
]);
});
test("a nullable _instance field fires", () => {
expect(detect(write("lib/cache.dart", "class Cache {\n static Cache? _instance;\n}"))).toEqual([{ line: 2 }]);
});
test("a singleton that was already there stays quiet", () => {
expect(detect(edit("lib/app_database.dart", database, { written: " int queries = 0;" }))).toEqual([]);
});
test("static const configuration stays quiet", () => {
expect(detect(write("lib/retry_policy.dart", "class RetryPolicy {\n static const int maxAttempts = 3;\n}"))).toEqual([]);
});
test("a field merely containing instance in its name stays quiet", () => {
expect(detect(write("lib/client_registry.dart", "class ClientRegistry {\n static final int instanceCount = 0;\n}"))).toEqual([]);
});
const dartApp = { project: "fixtures/dart_app" };
test("points at the principle when the user asks for a singleton in a Dart project", () => {
expect(detect(prompt("Make the API client a singleton"), dartApp)).toEqual([{ match: "singleton", depth: "pointer" }]);
});
test("stays quiet on the same words outside a Dart project", () => {
expect(detect(prompt("Make the API client a singleton"))).toEqual([]);
});