Testing runtime

A Runtime is meant to include all the dependencies required to run the app in a specific environment.

As such, we usually have different Runtime for testing, mocking, development, and production.

For example, in a nextjs app there is a "server" (for API routes and server components) and a "client" (for client components).

In my nextjs apps therefore I have two runtimes:

  • RuntimeServer for the server (which can contain also secret environment variables since it's not exposed to the client)
  • RuntimeClient for the client

In our example we can define a Runtime for testing using ManagedRuntime:

const TestConfigProvider = ConfigProvider.fromMap(
  new Map([["BASE_URL", "http://localhost:3000"]])
);

const ConfigProviderLayer = Layer.setConfigProvider(TestConfigProvider);
const MainLayer = PokeApi.Default.pipe(Layer.provide(ConfigProviderLayer));

const TestingRuntime = ManagedRuntime.make(MainLayer);

With this we can avoid having to use Effect.provide every time we want to run a test, and instead use TestingRuntime.runPromise directly:

const TestConfigProvider = ConfigProvider.fromMap(
  new Map([["BASE_URL", "http://localhost:3000"]])
);

const ConfigProviderLayer = Layer.setConfigProvider(TestConfigProvider);
const MainLayer = PokeApi.Default.pipe(Layer.provide(ConfigProviderLayer));

const TestingRuntime = ManagedRuntime.make(MainLayer);

const program = Effect.gen(function* () {
  const pokeApi = yield* PokeApi;
  return yield* pokeApi.getPokemon;
});

const main = program.pipe(Effect.provide(MainLayer)); 

it("returns a valid pokemon", async () => {
  const response = await TestingRuntime.runPromise(program);
  expect(response).toEqual({
    id: 1,
    height: 10,
    weight: 10,
    order: 1,
    name: "myname",
  });
});

This is all we need to be productive using our own Runtime. I found that ManagedRuntime solves all the use cases for most apps.

As mentioned, Runtime can get complex. Going into more details it's therefore out of scope for now.

With this we are officially done! We have all the pieces to build a complete app, fully type safe and tested.

The final module is all about more advanced effect patterns and modules and how to organize your effect app for production.