Node.js & JavaScript Testing Best Practices (2020)

⚠️🎊📢 Important Note: This Article became a GitHub repository with additional 15 best practices and community discussions

Short Intro

⚪️ 0. The Golden Rule: Testing must stay dead -simple and clear as day

***Section 1️⃣ : The Test Anatomy***

⚪ ️ 1. Include 3 parts in each test name

Include 3 parts in each test case, speak the product language

⚪️ 2. Describe expectations in a product language: use BDD-style assertions

⚪️ 3. Lint with testing-dedicated plugins

⚪️ 4. Stick to black-box testing: Test only public methods

️5. Choose the right test doubles: Avoid mocks in favor of stubs and spies

⚪️ 6. Don’t “foo”, use realistic input data

📗 Liked the content here and want more?

7. Test many input combinations using Property-based testing

⚪️ 8. Stay within the test: Minimize external helpers and abstractions

⚪️ 9. Avoid global test fixtures and seeds, add data per-test

⚪️ 10. Don’t catch errors, expect them

⚪️10. Tag your tests

11. Other generic good testing hygiene

***Section 2️⃣ : Test Types***

⚪️ 12. Enrich your testing portfolio: Look beyond unit tests and the pyramid

5 shiny testing techniques by Yoni Goldberg

⚪️ 13. Component testing might be your best affair

⚪️ 14. Ensure new releases don’t break the API using consumer-driven contracts

⚪️ 15. Test your middlewares in isolation

⚪️ 16. Measure and refactor using static analysis tools

⚪️ 17. Check your readiness for Node-related chaos

***Section 3️⃣: Measuring Test Effectiveness***

⚪️ 18. Get enough coverage for being confident, ~80% seems to be the lucky number

Istanbul coverage report
Contextual coverage

⚪️ 19. Inspect coverage reports to detect untested areas and other oddities

⚪️ 20. Measure logical coverage using mutation testing

Stryker report — Knowing that all or most of the mutations were killed gives much higher confidence than traditional coverage and the setup time is similar

***Section 4️⃣ : CI & Other Quality Measures***

⚪️ 21. Enrich your linters and abort builds that have linting issues

The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug

⚪️ 22. Shorten the feedback loop with local developer-CI

⚪️ 23. Perform e2e testing over a true production-mirror

deploy:
stage: deploy
image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
script:
- ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
- kubectl create ns $NAMESPACE
- kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
- mkdir .generated
- echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
- sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
- kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
- kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
environment:
name: test-for-ci

⚪️ 24. Parallelize test execution

Image result for testing parallel performance jest

⚪️ 25. Stay away from legal issues using license and plagiarism check

//install license-checker in your CI environment or also locally
npm install -g license-checker
//ask it to scan all licenses and fail with exit code other than 0 if it found unauthorized license. The CI system should catch this failure and stop the build
license-checker --summary --failOn BSD

⚪️26. Constantly inspect for vulnerable dependencies

⚪️ 27. Automate dependency updates

⚪️ 28. Other, non-Node related, CI tips

  1. Use a declarative syntax. This is the only option for most vendors but older versions of Jenkins allows using code or UI
  2. Opt for a vendor that has native Docker support
  3. Fail early, run your fastest tests first. Create a ‘Smoke testing’ step/milestone that groups multiple fast inspections (e.g. linting, unit tests) and provide snappy feedback to the code committer
  4. Make it easy to skim-through all build artifacts including test reports, coverage reports, mutation reports, logs, etc
  5. Create multiple pipelines/jobs for each event, reuse steps between them. For example, configure a job for feature branch commits and a different one for master PR. Let each reuse logic using shared steps (most vendors provide some mechanism for code reuse
  6. Never embed secrets in a job declaration, grab them from a secret store or from the job’s configuration
  7. Explicitly bump version in a release build or at least ensure the developer did so
  8. Build only once and perform all the inspections over the single build artifact (e.g. Docker image)
  9. Test in an ephemeral environment that doesn’t drift state between builds. Caching node_modules might be the only exception

⚪️ 29. Build matrix: Run the same CI steps using multiple Node versions

language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test

Thank You. Other articles you might like

--

--

Software Architect, Node.JS Specialist. Consultant, blogger, conference speaker, open source contributor — author of the largest Node.js best practices guide

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Yoni Goldberg

Software Architect, Node.JS Specialist. Consultant, blogger, conference speaker, open source contributor — author of the largest Node.js best practices guide