COSC2759 :linting and unit tests Lab Assessment
COSC2759 Lab/Tutorial Week 4 (2023)
Goals of this lab
- Buildon experience from week 3’s lab (GitHub Actions) and incorporate knowledge from week 4’s lab (automated testing).
- In this lab we’ll cover linting and unit tests. A full CI pipeline (e.g. for assignment 1)would include additional jobs, but you can start to see a pattern for you to follow to fill out those
Create yourself a test repo for this lab, and clone it
- Log in to GitHub in a web browser, and create a repo under your account. Call itanything, g. `lab4-test`. Under the heading “Initialize this repository with” tick “Add a README file”.
- Findthe ssh url of your new repo, it will look something like
`git@github.com:john-doe/lab4-test.git`.
- Clonethe repo with `git clone
` and `cd` into the repo’s working - Perhapsmake some change to the README file and push them up to GitHub (view them in the GitHub portal) to check you remember how to use git and have it set up (You can look at the lab 3 notes and the “git cycle” videos for more tips.)
Create a feature branch
- Inthis lab we’re going to create a GitHub Actions CI Create a new feature branch `git checkout -b feature/add-pipeline`.
- Pushthe new branch up to It will have the exact same commits as main, but push it up anyway just as a starting point. (Refer to lab 3 notes for detailed instructions if required.)
Fill in the sample app
You can’t have a CI pipeline without an app. We want to keep things simple so we’ll use a dummy app that just adds two numbers together.
- Create`sum.js` with the following contents:
function sum(a, b) { return a + b;
}
module.exports = sum;
- Create`sum.test.js` with the following contents:
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3);
});
test('adds 3 + 4 to equal 7', () => { expect(sum(3, 4)).toBe(7);
});
- Create`package.json` with the following contents:
{
"name": "lab-4-demo",
"version": "1.0.0",
"description": "",
"main": "sum.js", "scripts": {
"lint": "eslint .",
"test-unit": "jest"
},
"keywords": [],
"author": "",
"license": "", "devDependencies": {
"eslint": "^8.11.0",
"jest": "^27.5.1",
"jest-junit": "^13.0.0"
}
}
- Create`.eslintrc.yml` with the following contents:
env:
node: true commonjs: true es6: true jest: true
extends:
- eslint:recommended globals:
Atomics: readonly SharedArrayBuffer: readonly
parserOptions: ecmaVersion: 2018
rules:
no-trailing-spaces: "error"
- Create`jest.config.js` with the following contents:
module.exports = { reporters: [
'default',
['jest-junit', { outputDirectory: 'reports', outputName: 'report.xml' }],
],
};
- Finallycreate `.gitignore` with the following contents:
node_modules reports
This will ensure you don’t push your local copy of dependencies or test reports into the repo – they don’t belong in the repo.
- Run`npm install`. Then have a look at the `node_modules` What does
`npm install` do?
- Notethat we didn’t run `npm init`. What does `npm init` do?
Create the pipeline and add linting
- Firstwe will run the linter Run `npm run lint`. What does this do? How does
`npm` know what to do when you tell it to “run lint”?
- Nextwe add this job to the CI In your repo, create the file
`.github/workflows/ci-pipeline.yml` with the following contents:
name: Lab 4 CI Pipeline on:
push:
branches:
- mainpull_request:
branches:
- mainjobs:
lint:
runs-on: ubuntu-latest steps:
- uses:actions/checkout@v3
- name:Use js 18.x uses: actions/setup-node@v3 with:
node-version: "18"
- name:Install dependencies run: npm clean-install
- name:Run linter run: npm run lint
This creates a pipeline with a single job, called “lint”. The lint job contains several steps. Within each job, you have to install tools such as node, and you have to install your dependencies. Each job should be assumed to run on a new VM, so install your tools in every job, and push artifacts within the job too.
- Use`git add`, `git commit` and `git push` to package up your changes and copy them up to
- Goto the GitHub Actions tab and watch the pipeline
- Ifit fails, debug :-)
Unit testing
- Firstwe will run the unit tests Run `npm run test-unit`. (Do you notice a pattern between this section and the previous section?)
- Nextwe add this job to the CI In `.github/workflows/ci-pipeline.yml`, add a job to the existing “jobs” section” on to the end (careful with the indentation):
unit-tests:
runs-on: ubuntu-latest steps:
- uses:actions/checkout@v3
- name:Use js 18.x uses: actions/setup-node@v3 with:
node-version: "18"
- name:Install dependencies run: npm clean-install
- name:Run unit tests run: npm run test-unit
- if:success() || failure()
uses: actions/upload-artifact@v3 with:
name: unit-test-${{ github.sha }} path: reports/report.xml
Note that we tell GitHub Actions to upload the artifact regardless of success or failure in the preceding steps. By default, pipelines bail on the first sign of trouble; this is usually what you want. But with things like test reports, you want to override that default and stash the report telling you about the test failures. (Of course if your unit tests fail them you don’t want to go on to run integration tests or end-to-end tests. Just bail and give that feedback to the developer ASAP.)
- Use`git add`, `git commit` and `git push` to package up your changes and copy them up to
- Go to the GitHub Actions tab and watch the pipeline run. You might notice that thelinter and unit tests run in Is this what we want? Later in the lab we will learn how to insert dependency relationships to force jobs to run in a particular sequence.
- Ifit fails, debug :-)
- Canyou find the test report artifact?
Disabling jobs in a pipeline
- Editthe “lint” job to add an “if: false” property, like so:
lint:
if: false
runs-on: ubuntu-latest steps:
- uses:actions/checkout@v3
- name:Use js 18.x uses: actions/setup-node@v3 with:
node-version: "18"
- name:Install dependencies run: npm clean-install
- name:Run linter run: npm run lint
- Use`git add`, `git commit` and `git push` to package up your changes and copy them up to
- Goto the GitHub Actions tab and watch the pipeline What happened to the lint job?
- Thinkabout what other conditions you might put into the “if” property to control your pipeline
- Beforemoving on, revert the change you made to disable the “lint” (If you did it as a separate commit, you can `git revert` which is handy.) We’re removing the disabling. Double negatives. Wow. Can I make that more confusing? Recursion! Summary: we want the linter to start running again! :-)
Making a job dependent on another
- Makethe unit tests dependent on the linter by adding a “needs: lint” property to the unit test job, like so:
unit-tests:
needs: lint
runs-on: ubuntu-latest steps:
- uses:actions/checkout@v3
- name:Use js 18.x uses: actions/setup-node@v3 with:
node-version: "18"
- name:Install dependencies run: npm clean-install
- name:Run unit tests run: npm run test-unit
- if:success() || failure()
uses: actions/upload-artifact@v3 with:
name: unit-test-${{ github.sha }} path: reports/report.xml
- Use`git add`, `git commit` and `git push` to package up your changes and copy them up to
- Goto the GitHub Actions tab and watch the pipeline How does this pipeline run look different to previous runs?
Failure scenarios
- Canyou make the linter fail in the pipeline, just by changing `sum.js`?
- Canyou make the unit tests fail in the pipeline, just by changing `sum.js`?
- Canyou crash GitHub Actions and get root on a production server inside Microsoft? (OK this last one is a joke, before you do anything like that please see https://bounty.github.com/#rules)