Files
TSinjex/tests/Decorators.ts
Max P. 46c9a8b990 refactor(di): modularize and improve dependency injection for deno
- Consolidate import paths into scoped modules for better structure.
- Refactor decorators (`Inject`, `Register`) for improved type safety.
- Add `clear` method to the DI container for easier test cleanup.
- Introduce lazy initialization for registered instances.
- Add comprehensive unit tests for decorators and DI container.
- Standardize error handling and naming conventions for exceptions.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-02 19:55:16 +02:00

230 lines
6.3 KiB
TypeScript

// deno-coverage-ignore-file
// deno-lint-ignore-file no-explicit-any
import {
assertEquals,
assertInstanceOf,
assertStrictEquals,
assertThrows,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import { TSinjex } from "../src/classes/mod.ts";
import { Inject, Register } from "../src/decorators/mod.ts";
import { DependencyResolutionError } from "../src/interfaces/mod.ts";
const container = TSinjex.getInstance() as TSinjex;
Deno.test("should inject dependency when necessary is true", () => {
container.clear();
container.register("MockDependencyIdentifier", { value: "test-value" });
class TestClass {
@Inject("MockDependencyIdentifier")
private _dependency!: any;
public getDependency() {
return this._dependency;
}
}
const instance = new TestClass();
assertEquals(instance.getDependency().value, "test-value");
});
Deno.test("should inject dependency and run initializer", () => {
container.clear();
container.register("MockDependencyIdentifier", { value: "test-value" });
class TestClass {
@Inject("MockDependencyIdentifier", (x: any) => {
x.value = "test-value-init";
return x;
})
dependency!: any;
public getDependency() {
return this.dependency;
}
}
const instance = new TestClass();
assertEquals(instance.getDependency().value, "test-value-init");
});
Deno.test("should throw error if initializer fails and dependency is necessary", () => {
container.clear();
container.register("InitThrowDependencie", { value: "test-value" });
class TestClass {
@Inject<TestClass, any, any>("InitThrowDependencie", () => {
throw new Error("Initializer error");
}, true)
dependency!: any;
public getDependency() {
return this.dependency;
}
}
assertThrows(
() => {
const instance = new TestClass();
instance.getDependency()();
},
Error,
"Initializer error",
);
});
Deno.test("should throw DependencyResolutionError if dependency not found", () => {
container.clear();
class TestClass {
@Inject("NonExistentDependencyIdentifier")
private _dependency!: any;
public getDependency() {
return this._dependency;
}
}
assertThrows(() => {
const instance = new TestClass();
instance.getDependency()();
}, DependencyResolutionError);
});
Deno.test("should replace the property with the resolved dependency", () => {
container.clear();
container.register("MockDependencyIdentifier", { value: "test-value" });
class TestClass {
@Inject("MockDependencyIdentifier")
private _dependency!: any;
public getDependency() {
return this._dependency;
}
public isDependencyTypeofFunction() {
return typeof this._dependency === "function";
}
}
const instance = new TestClass();
assertEquals(instance.getDependency().value, "test-value");
assertEquals(instance.isDependencyTypeofFunction(), false);
assertEquals(instance.getDependency().value, "test-value");
});
Deno.test("Register Decorator: should register a dependency", () => {
container.clear();
@Register("MockDependencyIdentifier")
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
}
assertStrictEquals(
container.resolve("MockDependencyIdentifier"),
TestClass,
);
});
Deno.test("RegisterInstance: should register an instance of a dependency", () => {
container.clear();
@Register("InstanceIdentifier", (x) => new x())
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = "instance";
}
const resolved = container.resolve<TestClass>("InstanceIdentifier");
assertEquals(resolved?.mark, "instance");
});
Deno.test("RegisterInstance: should run init function during registration", () => {
container.clear();
@Register("InstanceIdentifier", (x: new () => TestClass) => {
const instance = new x();
instance.mark = "init";
return instance;
})
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = "instance";
}
const resolved = container.resolve<TestClass>("InstanceIdentifier");
assertEquals(resolved?.mark, "init");
});
Deno.test("RegisterInstance: instance should persist modifications", () => {
container.clear();
@Register("InstanceIdentifier", (x) => new x())
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = "instance";
public test: string = "test";
}
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
if (instance1 == null) {
throw new Error("Instance1 is null");
}
instance1.test = "test2";
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
if (instance2 == null) {
throw new Error("Instance2 is null");
}
assertEquals(instance2.test, "test2");
});
Deno.test("RegisterInstance: init function should persist modifications", () => {
container.clear();
@Register("InstanceIdentifier", (x: new () => TestClass) => {
const instance = new x();
instance.mark = "init";
return instance;
})
class TestClass {
private readonly _dependency!: any;
public getDependency() {
return this._dependency;
}
public mark: string = "instance";
public test: string = "test";
}
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
if (instance1 == null) {
throw new Error("Instance1 is null");
}
instance1.test = "test2";
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
if (instance2 == null) {
throw new Error("Instance2 is null");
}
assertEquals(instance2.test, "test2");
});