From ee0cabbd1d9e0c1e68d27acd6b7bbf41653a86fd Mon Sep 17 00:00:00 2001 From: Dedekind561 Date: Tue, 9 Apr 2024 10:00:50 +0100 Subject: [PATCH] add guides for testing and code quality --- guides/code-quality/readme.md | 110 ++++++++++++++++++++++++++++++++++ guides/testing/readme.md | 90 ++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 guides/code-quality/readme.md create mode 100644 guides/testing/readme.md diff --git a/guides/code-quality/readme.md b/guides/code-quality/readme.md new file mode 100644 index 0000000000..0d0c4e2984 --- /dev/null +++ b/guides/code-quality/readme.md @@ -0,0 +1,110 @@ +# Code quality improvements (Optional) + +While the following work items are completely optional, they will help you to make fewer mistakes as you continue with future requirements. It is generally a good idea to have them when working on projects of any size. + +## 1) Proper error handling + +You should make your system to be designed with error handling in mind. For example if the database cannot be accessed when you call `GET /api/videos`, then your backend endpoint should return a properly formatted error message with a HTTP `500` error code. + +Here is an example response: + +```json +{ "success": false, "error": "Could not connect to the database" } +``` + +**Note:** You can design how you return error messages differently than the above example. You could also try and merge the error and non-error response styles into one. For example the standard `200` response on the same endpoint could be something like the following: + +```json +{"success":true,"videos":[(...)]} +``` + +Once you do this on the backend you should also change your frontend, to make sure it handles errors that are received. For example if your frontend receives an error like above it might show a message like `"Could not connect to the database, please reload the page at a later time"`. + +**Note:** Once you add this feature, make sure you keep handling errors properly during the week 2 and week 3 requirements as well. + +## 2) Prettying and linting + +It is also usually a good idea to make sure that your code is formatted based on a single standard throughout, and also passes basic sanity checks. There are two projects that can usually help you with that: + +- `prettier` is a formatter that makes sure that your code is formatted the same way throughout. For example all files use `tab` characters for indenting. +- `eslint` is a linter that checks the code for common coding mistakes and warns you if it encounters any of them. It can also automatically fix some mistakes. + +Let's set up both of them! + +### `prettier` + +First install prettier into your `package.json` file: + +```sh +npm install prettier --save-dev +``` + +Next you will need a `.prettierrc` file in the root directory. We have already provided one for your convenience. + +You can now run prettier to check your files: + +```sh +npm exec prettier -- --check . +``` + +And also to automatically fix them: + +```sh +npm exec prettier -- --write . +``` + +If you don't want to type out these commands you can add them as `scripts` into your `package.json` file. For example you can add a line like: + +```json + "prettier": "prettier --write ." +``` + +to the scripts section, and then you'll be able to automatically pretty your files by typing: + +```sh +npm run prettier +``` + +### `eslint` + +Installing `eslint` is similar, but to get the most out of it you will need to install multiple projects: + +```sh +npm install eslint eslint-config-prettier eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-refresh eslint-plugin-react-hooks eslint-plugin-n eslint-plugin-jest eslint-plugin-jest-dom eslint-plugin-vitest eslint-plugin-testing-library @codeyourfuture/eslint-config-standard --save-dev +``` + +This will install `eslint`, and a couple plugins that help you with validating tests, JSX code and React components, like proper `useEffect` usage. + +You will also need to have multiple `.eslintrc.json` files, one for each project, tailored to that project's needs. These have also all been provided already. + +Once you have everything in place you can run the linter to check for common code mistakes: + +```sh +npm exec eslint . +``` + +You can also ask it to fix any issues it can automatically fix: + +```sh +npm exec eslint -- --fix . +``` + +Same as for `prettier`, you might want to add these commands to your `package.json` for easier access. + +### Checks during PRs + +It is a good idea to enforce running both the linter and prettier during PRs. One way to do that is to make sure GitHub runs these checks for every PR, blocking the PR in case the code doesn't pass these checks. We have already prepared the `.github/workflows/enforce-linting.yml` file that you can use to run checks on GitHub manually. If you also uncomment the `pull_request` line, you will enable GitHub to run these checks automatically on every PR. + +To confirm, the top of the file should look like this: + +```yaml +on: + workflow_dispatch: + pull_request: +``` + +**Note:** Make sure to have a full read of this file and try to figure out what it does. + +## Next Level + +Once finished you can go to [Level 200](./200.md) diff --git a/guides/testing/readme.md b/guides/testing/readme.md new file mode 100644 index 0000000000..80253e4b46 --- /dev/null +++ b/guides/testing/readme.md @@ -0,0 +1,90 @@ +# Automated tests (Optional) + +The below points are improvement ideas that you can add to your project to become a great project. If you want this project to be part of your portfolio we suggest that you implement at least some of the optional features. You don't need to do all of them during this week, you are free to revisit this list later + +## 1) Backend unit tests + +Automated tests are code that, when run, will check that your code adheres to some pre-set conditions, usually your acceptance criteria. With the help of automated tests you can make sure that even if you add new features to your code, your existing features remain working. Breaking existing features when adding new ones is very common in the engineering world, there is a word for it as well: "regression". + +Unit tests only check a small subset (a unit) of your application at a time. These tests can usually be both written and run quickly so they are a good way of determining that the application is doing what it should be doing. + +You can test your backend in isolation, your frontend in isolation and you can also have tests that check both of them at the same time. For this level, we will start by adding tests for your backend. + +### Setting up backend unit tests ⚙️ + +We have helped you get started by setting up a test runner for your backend systems and adding a couple tests to `server/api.test.js`. These tests would check that the list and delete endpoints works as expected. + +First you need to set up the tests. To do that make sure you create a separate database for tests. For example let's call it `videorec_test`: + +```sh +createdb videorec_test +``` + +Afterwards edit your `.env` file and change the `TEST_DATABASE_URL` to point to this database. + +**Note:** we are using a different database to make sure that we don't accidentally modify your normal database every time when you run your tests. + +Once you have set up the database settings you can now run the tests by calling + +```sh +npm run test:server +``` + +Check the response, it will tell you if the tests have succeeded or not. It is possible that you will need to update the test to cater for how you have implemented your backend. + +### Transactional tests + +The test runner above is set up to have transactional tests. What this means is that whenever you start the testing session, the code will reset your (test) database using the `initdb.sql` you have created in [level 110](./110.md). Afterwards it will run each test in a database transaction, rolling it back at the end of the test. Database transactions are a feature of most relational databases that allows you to run SQL commands in a temporary environment first, and only if you are happy with the results will they be saved to the database. It's a bit like editing a file and not saving it until you are happy with the result. + +We use this feature during test runs. We effectively ask the database to never save our changes at the end of the tests. This allows each test case to start with the exact same, initial database as every other test. For example when testing the video removal features, during the test the video will be removed, but once the tests are run, the database is reset, and subsequent tests will still be able to access the deleted video. + +### Enable PR tests + +Tests are made much more useful if they run every time you create a PR. This way GitHub will make sure to run them and let you know if your latest code is not working anymore based on the tests. To enable backend tests you should to `.github/workflows/run-server-tests.yml` and remove the comment from the line that says `pull_request:`. This will run the `npm run test:server` call every time you create a new PR blocking merging in case tests fail. + +To confirm, the top of the file should look like this: + +```yaml +on: + workflow_dispatch: + pull_request: +``` + +This will allow GitHub to run the defined action on each pull request, and you can also run it ad-hoc from the "Actions" page whenever you wish. + +**Note:** Make sure to have a full read of this file and try to figure out what it does. + +### Adding additional tests + +Based on the existing tests in the system you could try and create one that checks the add video functionality we added during [level 210](./210.md) as well. It should verify that videos are added if the request is formatted properly, but fail if the request is missing the title, or the URL is incorrect. + +## 2) Frontend unit tests + +Similar to the backend unit tests we can create tests that verify that the frontend is working as expected in an automated way. + +### Setup + +To setup frontend tests we have helped you get started by adding a couple tests to `client/test/App.test.js`. These would check that the page, after loading, contains enough `