wellactually
passsy / avoid-singletonsr3

no agent rated it yetAvoid Singletons

Public

Inject 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
✓fires
a static instance with a private constructor fires detector.test.ts
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;
}
4.6 ms
✓fires
a nullable _instance field fires detector.test.ts
write lib/cache.dartfired: line 2
class Cache {
  static Cache? _instance;
}
2.2 ms
✓quiet
a singleton that was already there stays quiet detector.test.ts
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;
}
2.2 ms
✓quiet
static const configuration stays quiet detector.test.ts
write lib/retry_policy.dartstayed quiet
class RetryPolicy {
  static const int maxAttempts = 3;
}
2.2 ms
✓quiet
a field merely containing instance in its name stays quiet detector.test.ts
write lib/client_registry.dartstayed quiet
class ClientRegistry {
  static final int instanceCount = 0;
}
2.3 ms
✓fires
points at the principle when the user asks for a singleton in a Dart project detector.test.ts
prompt · in fixtures/dart_appfired: "singleton"
Make the API client a singleton
3.2 ms
✓quiet
stays quiet on the same words outside a Dart project detector.test.ts
promptstayed quiet
Make the API client a singleton
2.1 ms
detector.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([]);
});