You can use 2 functions for testing - test and it.
- They are exactly the same, more precisely
itis an alias fortest(const it = test;) - Choose one of them and use it in your project (keep consistency)
- They differ in the way they are read:
test('if it does this thing', () => {});it('should do this thing', () => {});
"If it"or"should"can be removed:test('does this thing', () => {});or more likelytest('thing does this', () => {});it('does this thing', () => {});
// ❌ Do not put too vague names that do not provide about what is being tested
it('works correctly', () => {
// use expect() here
});// ✅ Rather put specific information about what is being tested
it('sums two numbers', () => {
// use expect() here
});Do not do assertions by yourself, because testing libraries implements it more likely in a different way.
toBeis not using===for comparison, but it is usingObject.is
// ❌ DO NOT write comparison by yourself
expect(5 === 5).toBe(true);// ✅ Rather let testing framework to compare
expect(5).toBe(5);Implementation details are things which users of your code will not typically use, see, or even know about.
- End-users and developers are the two "users" that our application code needs to consider.
class Store {
_data = [];
update = (key, value) => {
this._data[key] = value;
};
get = (key) => this._data[key];
}
const store = new Store();
store.update('age', 18);
// ❌ Do not test API that users (developers) should not use
expect(store._data.age).toBe(18);// ✅ Rather test public API that users actually use
expect(store.get('age')).toBe(18);Each test should focus on a single functionality or behavior.
// ❌ DO NOT test multiple functionalities in one test
it('creates and fetchs user', () => {
createUser({ id: 1, name: 'John' });
const user = fetchUser(1);
expect(user.name).toBe('John');
deleteUser(1);
expect(fetchUser(1)).toBeNull();
});// ✅ Rather split tests into separate units
it('creates user', () => {
createUser({ id: 1, name: 'John' });
const user = fetchUser(1);
expect(user.name).toBe('John');
});
it('deletes user', () => {
createUser({ id: 1, name: 'John' });
deleteUser(1);
expect(fetchUser(1)).toBeNull();
});Most of the time it is not possible to test every case. But try to think about edge cases that might happen.
💡 Note that some edge cases related to:
- Testing data types like: null, undefined, ...
- Testing mutations (changes) to the original object/array
could be skipped when you are using type safe system like TypeScript
// ❌ DO NOT ignore edge cases
it('converts string to uppercase', () => {
expect(toUpperCase('hello')).toBe('HELLO');
});// ✅ Rather test various edge cases
it('converts string to uppercase', () => {
expect(toUpperCase('hello')).toBe('HELLO');
});
it('handles empty strings', () => {
expect(toUpperCase('')).toBe('');
});
it('handles strings with numbers and special characters', () => {
expect(toUpperCase('0hello123!')).toBe('0HELLO123!');
});
it('handles strings with mixed case', () => {
expect(toUpperCase('HeLLo')).toBe('HELLO');
});
it('handles strings with leading and trailing spaces', () => {
expect(toUpperCase(' hello ')).toBe(' HELLO ');
});
it('handles strings with only spaces', () => {
expect(toUpperCase(' ')).toBe(' ');
});