I have a test:
describe("Given a config repository", () => {
let target: ConfigRepository;
beforeEach(() => {
target = InMemoryConfigRepository();
});
test("When creating a new config, Then it is persisted properly", async () => {
const key = "key";
const namespace = "namespace";
const result = await target.upsert({
key,
namespace,
value: "value",
teamId: "teamId",
})();
const configs = await target.findAll()();
expect(result._tag).toEqual("Right");
expect(configs.length).toBe(1);
expect(configs.map((c) => c.key)).toEqual([key]);
});
});
that can be applied against an implementation of my interface:
export type ConfigRepository = {
get: <T extends RequiredJsonObject>(
props: ConfigurationProperties<T>
) => TE.TaskEither<DatabaseError | MissingConfigError, ConfigEntity[]>;
findAll(): T.Task<ConfigEntity[]>;
upsert: (
config: UnsavedConfig
) => TE.TaskEither<DatabaseError, ConfigEntity>;
};
My problem is that I don't see a clear way of applying this to different implementations of the same interface. How can I test say a PrismaConfigRepository
with the same test?
You probably want to to whats called a Parameterized Test. One way to implement this is to use .each. The idea is then to pass the constructor of each of your classes implementing the ConfigRepository
interface as a parameter, and constructing the instances using the constructor in beforeEach
(rather than always instanciating an InMemoryConfigRepository
).
Rough sketch of what this could look like:
describe.each(
[[InMemoryConfigRepository],[PrismaConfigRepository]]
)(
"Given a config repository (implementation no. %#)",
(configRepositoryConstructor: new () => ConfigRepository) => {
let target: ConfigRepository;
beforeEach(() => {
target = configRepositoryConstructor();
});
test("When creating a new config, Then it is persisted properly", async () => {
// current test implementation
});
}
);