- Automated Testing Introduction
- The Jest Framework
- Getting Started
- Testing Examples
- Popular Jest Matcher Functions
- The Vitest Framework
- Getting Started
- Watch Mode
- Inline Tests
- PyTest for Python
- Getting Started
Automated Testing
Automated testing is the practice of writing code to test our code.
Automated tests help us deliver software with fewer bugs and of better quality. They also help us refactor our code with confidence.
Tests should not be too general nor too specific. If they’re too general, they don’t give you much confidence that your code works. If they’re too specific, they become fragile and can break easily. As you write code, you have to spend extra unnecessary time to fix these broken tests.
Mocking is replacing a real implementation of a function with a fake or mock function. It allows us to isolate our application code from its external resources.
There are 3 types of automated tests:
Unit tests: test a unit of an application without external resources (eg db)
Integration tests: test the application with external resources.
Functional or end-to-end tests: test the application through its UI
In this post, we will be taking a look into unit testing with several frameworks like jest, vitest, and pytest.
The Jest Framework
Jest is a new trending popular testing framework recommended by Facebook. It comes with everything you need to write automated tests.
Getting Started
To get started with Jest, first run the following command:
npm install --save-dev jest
"We save this as a dev dependency because we don't want to use this for production."
Next, add the following to your package.json:
...
"scripts": {
"test": "jest"
}
Testing Examples
Now we should be able to write our tests. Our test files need to have a .test or .spec as a part of its naming convention. Ex. sum.test.js.
Here's an example of how Jest is used to test a hypothetical function from our sum.js file.
//sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
We create a file named sum.test.js and test our code.
const sum = require('./sum'); //We import the file that we're testing
//We use test() followed by the name of our test and what we expect
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
//We expect sum(1,2) to be equal to 3. If not, we get a failing test
});
Keep in mind that we want to make sure we test all of our executions. Let's take a look at another example:
//lib.js
//Here we have 3 return statements we should test
module.exports.absolute = function(number) {
if (number > 0) return number;
if (number < 0) return -number;
return 0
}
//lib.test.js
const lib = require('../lib')
//We use describe() to group together our tests
describe('absolute', () => {
//it() works the same way as test(), but just more readable
it('should return a positive number if input is positive', ()=>{
const result = lib.absolute(1)
expect(result).toBe(1)
});
it('should return a positive number if input is negative', ()=>{
const result = lib.absolute(-1)
expect(result).toBe(1)
});
it('should return 0 if input is 0', ()=>{
const result = lib.absolute(0)
expect(result).toBe(0)
});
})
To check to see if all of our lines are being tested, we can also request a coverage report. We do this by adding --coverage to our script tag.
...
"scripts": {
"test": "jest --coverage"
}
Popular Jest Matcher Functions
// Equality
expect(...).toBe();
expect(...).toEqual();
expect(...).not.toEqual();
// Truthiness
expect(...).toBeDefined();
expect(...).toBeNull();
expect(...).toBeTruthy();
expect(...).toBeFalsy();
// Numbers
expect(...).toBeGreaterThan();
expect(...).toBeGreaterThanOrEqual();
expect(...).toBeLessThan();
expect(...).toBeLessThanOrEqual();
// Strings
expect(...).toMatch(/regularExp/);
// Arrays
expect(...).toContain();
// Objects
expect(...).toBe(); // check for the equality of object references
expect(...).toEqual(); // check for the equality of properties
expect(...).toMatchObject();
// Exceptions
expect(() => { someCode }).toThrow();
The Vitest Framework
Vitest is an enhanced version of Jest that has many additional benefits such as speed, watch mode, being able to write inline tests, and many more.
Getting Started
To get started with Vitest, first run the following command:
npm install --save-dev vitest
"We save this as a dev dependency because we don't want to use this for production."
Next, add the following to your package.json:
...
"scripts": {
"test": "vitest"
}
Watch Mode
Since vitest is constantly on watch mode, whenever there are new file changes it'll update automatically to see if tests are still passing or failing. This on top of the blazing fast speed to run test can be very useful.
You can also use --coverage to get a full coverage which will also be on watch mode.
Inline Tests
Using Vitest, you can write inline tests within the same file which enhances the feedback loop for development.
To get started, put a if (import.meta.vitest) block at the end of your source file and write some tests inside it. For example:
// src/index.ts
// the implementation
export function add(...args: number[]) {
return args.reduce((a, b) => a + b, 0)
}
// in-source test suites
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest
it('add', () => {
expect(add()).toBe(0)
expect(add(1)).toBe(1)
expect(add(1, 2, 3)).toBe(6)
})
}
However, you will need to update your vite.config file to grab the correct files under src/: To do this, you can add a config file that looks something like this:
// vite.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
includeSource: ['src/**/*.{js,ts}'],
},
})
Another issue is that if your file goes into production, you will want to prevent it from grabbing all the test code to avoid slowing down the application. To fix this, you will need to set the define options in your config file, letting the bundler do the dead code elimination.
// vite.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
define: {
'import.meta.vitest': 'undefined',
},
test: {
includeSource: ['src/**/*.{js,ts}']
},
})
Read More on Vitest Features Here: Features | Guide | Vitest
PyTest for Python
The pytest framework makes it easy to write small, readable tests, and can scale to support complex functional testing for applications and libraries.
Getting Started
To get started with pytest, first run the following command:
pip install -U pytest
Implement your tests with assert which will check if the given statement is true or false. If it throws an exception, the test will fail.
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
Once you develop multiple tests, you can group them into a class. Pytest makes it easy to create a class containing more than one test:
# content of test_class.py
class TestClass:
def test_one(self):
x = "this"
assert "h" in x
def test_two(self):
x = "hello"
assert hasattr(x, "check")
Read More on PyTest Here: Get Started — pytest documentation
Comments