> For the complete documentation index, see [llms.txt](https://kinesis-school-of-programming.gitbook.io/nestjs-unleashed/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kinesis-school-of-programming.gitbook.io/nestjs-unleashed/extra-module-6-automated-testing/e2e-tests/simulated-environment.md).

# Simulated environment

An environment to simulate an actual real-world scenario.

Well, before proceeding to the users tests, let's have a test database in order to test the CRUD operations. An actual **Postgres database** will be used so that the tests are closer to a real-world scenario. **Environment variables** will also be used for better organization.

We should then perform the following inside the <mark style="color:purple;">test</mark> folder:

* Create a <mark style="color:purple;">.env</mark> file, and copy to it the **database credentials** from the root <mark style="color:purple;">.env</mark>

{% hint style="info" %}
Set the <mark style="color:blue;">`DATABASE_PORT`</mark> to <mark style="color:blue;">5433</mark>, as to prevent a collision with the development database.
{% endhint %}

* Create a <mark style="color:purple;">compose.yml</mark> file, and copy to it the **database** service from the root <mark style="color:purple;">compose.yml</mark>

{% hint style="info" %}
Remove all the options related to **volumes**, as there's no need to persist data after the tests finish.
{% endhint %}

And in the file <mark style="color:purple;">package.json</mark>, let's add <mark style="color:blue;">pre</mark> and <mark style="color:blue;">post</mark> scripts for <mark style="color:blue;">`test:e2e`</mark> around it. They will be responsible for, respectively, **creating** and **excluding** the test database.

```json
"pretest:e2e": "docker compose -f test/compose.yml --env-file test/.env up -d",
"posttest:e2e": "docker compose -f test/compose.yml --env-file test/.env rm -sfv",
```

{% hint style="info" %}
Some notes:

* The paths to <mark style="color:purple;">compose.yml</mark> and <mark style="color:purple;">.env</mark> files are defined with <mark style="color:orange;">-f</mark> and <mark style="color:orange;">--env-file</mark>, respectively
* Better explaining the <mark style="color:orange;">rm</mark> (**remove**) options:
  * <mark style="color:orange;">-s</mark>: stop the container before removing it
  * <mark style="color:orange;">-f</mark>: force the operation (no confirmation)
  * <mark style="color:orange;">-v</mark>: delete the anonymous volume
* If the tests <mark style="color:red;">fail</mark>, the test database will need to be deleted manually with the <mark style="color:blue;">post</mark> script
  {% endhint %}

And adjust the main script to use them.

```json
"test:e2e": "yarn pretest:e2e && vitest run --config ./vitest.config.e2e.ts && yarn posttest:e2e",
```

We may also organize this in another way, using a different naming convention to be more explicit and also an "orchestrator" script that executes the other ones.

```json
"test:e2e:setup": "docker compose -f test/compose.yml --env-file test/.env up -d",
"test:e2e:run": "vitest run --config ./vitest.config.e2e.ts",
"test:e2e:teardown": "docker compose -f test/compose.yml --env-file test/.env rm -sfv",
"test:e2e": "yarn test:e2e:setup && yarn test:e2e:run && yarn test:e2e:teardown",
```

Well, it is an improvement indeed... but we can still see some flaws:

* The base docker command is identical in both the **setup** and **teardown** scripts
* In case of test <mark style="color:red;">failure</mark>, we still need to run the **teardown** script manually

There is an even better way. We may use a Vitest feature called **global setup**, which involves a file that manages exactly the steps of **setup**/**teardown** that we are dealing with. Also, even in case of errors, the teardown phase will always execute. Let's then use this feature for the **e2e tests**.

Create this file in <mark style="color:purple;">test</mark>/<mark style="color:purple;">global-setup.e2e.ts</mark>. First, we'll define the base docker compose command.

```typescript
const BASE_COMMAND = 'docker compose -f test/compose.yml --env-file test/.env';
```

After that, a function that appends the actual command to it and executes it.

```typescript
const compose = (command: string) => {
  execSync(`${BASE_COMMAND} ${command}`, { stdio: 'inherit' });
};
```

{% hint style="info" %}
Some notes:

* <mark style="color:blue;">`execSync`</mark> executes commands sequentially
* <mark style="color:blue;">`stdio: 'inherit'`</mark> keeps terminal output as it was
  {% endhint %}

With this, we have everything to create our <mark style="color:blue;">`setup()`</mark>/<mark style="color:blue;">`teardown()`</mark> functions, which will be very simple.

```typescript
export function setup() {
  compose('up -d');
}

export function teardown() {
  compose('rm -sfv');
}
```

Now, to make this work, we just need to set the path to the <mark style="color:blue;">`globalSetup`</mark> in <mark style="color:purple;">vitest.config.e2e.ts</mark>...

```typescript
globalSetup: './test/global-setup.e2e.ts',
```

...and in <mark style="color:purple;">package.json</mark>, once again have only that original <mark style="color:blue;">`test:e2e`</mark> script.

```json
"test:e2e": "vitest run --config ./vitest.config.e2e.ts",
```

Continuing, inside the <mark style="color:purple;">src</mark> folder, we can then create a **configuration namespace** for the test database in <mark style="color:purple;">testing</mark>/<mark style="color:purple;">config</mark>/<mark style="color:purple;">test-database.config</mark>.

```typescript
export default registerAs('testDatabase', () => {
  const user = env.DATABASE_USER!;
  const password = env.DATABASE_PASSWORD!;
  const host = env.DATABASE_HOST!;
  const port = env.DATABASE_PORT!;
  const name = env.DATABASE_NAME!;

  const url = `postgresql://${user}:${password}@${host}:${port}/${name}`;

  const config = {
    type: 'postgres',
    url,
    autoLoadEntities: true,
    synchronize: true,
  } as const satisfies TypeOrmModuleOptions;
  return config;
});
```

{% hint style="info" %}
Here, we turn on <mark style="color:blue;">`synchronize`</mark> to automatically execute the migrations.
{% endhint %}

And we can create the file <mark style="color:purple;">testing</mark>/<mark style="color:purple;">util</mark>/<mark style="color:purple;">testing.constants</mark> with the **validation schema** for the testing environment variables.

```typescript
export const TEST_ENV_VALIDATION_SCHEMA = z.object({
  DATABASE_USER: z.string(),
  DATABASE_PASSWORD: z.string(),
  DATABASE_HOST: z.hostname(),
  DATABASE_PORT: ze.port,
  DATABASE_NAME: z.string(),
});
```

We may then proceed to the actual tests.
