Step 12: end to end tests for backend

A few more commands have been added to our package.json, see README.md:

To work on the frontend with stubs for the API endpoints, run npm start. To simulataneously work on the frontend code and frontend end to end tests, use npm run start:with-e2e – this should be the default mode when working on the frontend.

To work on the backend with core API endpoints stubbed out, use npm run start:backend. To also run backend end to end tests, use npm run start:backend:with-e2e – this should be the default mode when working on the backend.

You can also run both frontend and backend locally, use npm run start:local to serve frontend along with the backend, this will work with the local PostgreSQL database, with core endpoints stubbed out.

To validate SSR, you can run npm run build:ssr and then npm run serve:ssr – this will run SSR build against the backend API stubs. To run SSR build with the backend, run npm run build:ssr:local and then npm run serve:ssr:local.

So we can now develop backend in the same fashion as the frontend – while running simultaneously the app itself and the end to end tests for it. This is really great as we can cover all code with thorough tests as we go now. A cool bonus is that when the backend e2e tests are run with npm run tests (which in turn executes npm run backend-e2e) they are executed with SSR-enabled frontend – this ensures our SSR build doesn’t break.

Let’s take a look at the actual test we added – we only created one in this PR, see auth.spec.ts:

This is very similar to our frontend e2e tests, the difference is that it runs on the frontend that talks to the actual backend and not just the stub server. We are still using the stub server to fake the core, which we simply don’t have yet, but the backend is real otherwise, it’s connected to the local PostgreSQL database.

With these tests, we can exercise all logical branches of our backend, and make assertions not only on the web page, but in the database as well. This is done with cyExecSql function, see backend-e2e-helpers.ts:

It just posts a free-form SQL query to the new /exec-sql endpoint, see exec-sql.ts:

Obviously we don’t want this endpoint exposed in production ๐Ÿ™‚ We don’t even connect the router unless we’re running in a development environment, see router.ts:

We also tighten our CORS policy by only allowing requests coming from the frontend, see main.ts:

Notice how we are able to make assertions about sent emails, and also to extract the activation link from the email by saving all sent emails to a new table in the database, see send-email.ts:

This is good not just for the tests, it can be very helpful dealing with customer requests – it’s just really nice to have all emails stored in the database, they are sometimes useful, and we can wipe out the old ones if they start taking up too much space. The table is defined in emails.ts:

We create it with migration 200104-emails.ts:

Note the sentToEmail field – since users can change their email address, it’s prudent to persist the actual email we sent the formatted template to at the moment of sending.

There was actually a problem – we would add a new user record every time a new email is submitted, even if a user with this email was already registered, but not activated, i.e. the email was saved in unconfirmedEmail field. Fixing bugs without adding a test is a bad practice – the test above will ensure the bug stays fixed forever. The fix itself is found in auth.ts:

If the user is already registered, we will just update their password – and only if the email is new will we add a record.

There was another problem this PR fixes – when loading a page from SSR app, the page would animate unnecessarily after being fully rendered. The fix is in app.component.ts:

SSR build was actually broken… won’t happen again as npm run tests validates the SSR now ๐Ÿ™‚

As per usual, do not hesitate if you have questions or feedback.

Cheers,
Andrew