Skip to content

Latest commit

 

History

History
184 lines (107 loc) · 4.72 KB

testing.md

File metadata and controls

184 lines (107 loc) · 4.72 KB

Testing Style Guide

go/testing-style-guide

1. Test cases

1.1. Single test case per scenario

When you add a test ask yourself if this test is required and why. Is there any other test checking the same functionality? If you can't find a good reason for the test to exist - don't write it.

1.2. Valid and short test case

There should be only necessary and valid steps in a single test case. If a single test case contains too many test steps this may lose its aim.

// ❌ bad
it(`should work`, () => {
 expect(sum(2, 3)).toBe(5);
 expect(sum(0, 0)).toBe(0);
 expect(sum(-1, 1)).toBe(0);
 expect(sum(-1, 0)).toBe(-1);
})

// ✅ good
it(`should return a sum of positive numbers`, () => {
 expect(sum(2, 3)).toBe(5);
});

it(`should return 0 when adding zeroes`, () => {
 expect(sum(0, 0)).toBe(0);
});

it(`should return 0 when adding additive inverses `, () => {
 expect(sum(-1, 1)).toBe(0);
});

it(`should return a negative number when adding negative and zero`, () => {
 expect(sum(-1, 0)).toBe(-1);
});

1.3. Fill in the 'describe' block

Write a module name that is tested in the describe block for the unit and integration test cases.

// ❌ bad
describe('', () => {
 it(`Function 'sum' should return 0 if adding zeroes`, () => {})
 it(`Function 'sum' should have property 'age'`, () => {})
 it(`Function 'sum' should return 0 if called without arguments`, () => {})
});

// ✅ good
describe(`Function 'sum':`, () => {
 it(`should return 0 if adding zeroes`, () => {})
 it(`should have property 'age'`, () => {})
 it(`should return 0 if called without arguments`, () => {})
});

1.4. Test case descriptions should follow a pattern:

should [EXPECTED_RESULT] when [STATE]. With filled in describe block each test case description should start with lowercase.

// ❌ bad
it('Works without arguments', () => {})

// ✅ good
it('should return 0 when called without arguments', () => {})

1.5. Every single test case should explain what should be done.

// ❌ bad
it(`adds zeroes and returns 0`, () => {});
it(`has property 'age'`, () => {});
it(`returns 0 if called without arguments`, () => {});

// ✅ good
it(`should return 0 if adding zeroes`, () => {});
it(`should have property 'age'`, () => {});
it(`should return 0 if called without arguments`, () => {});

1.6. Use multiple describe blocks if you test different things

// ❌ bad
describe('', () => {
  it('BaseRobot class should create a robot', () => {});
  it('FlyingRobot class should create a flying robot', () => {});
});

// ✅ good
describe('BaseRobot class', () => {
  it('should create a robot', () => {});
});

describe('FlyingRobot class', () => {
  it('should create a flying robot', () => {});
});

1.7. Backend: Use test factories instead of creating instances explicitly

Why? To be tool-agnostic. It's possible to update factories in one place instead of checking every test-case separately

💡 Note: If your project doesn't have factories infrastructure, time to create it

// ❌ bad
const user = await User.create();

// ✅ good
const user = await userFactory.create();

1.8. Backend: avoid fetching values from database in tests using ORM

Why? Again, to be tool-agnostic and don't rely on existing infrastructure. One day ORM may be changed which may lead to refactoring the whole tests infrastructure

await client.signUp({ email: '[email protected]' });

// ❌ bad
const user = await UserModel.findOne({ where: { email: '[email protected]' }}) // direct DB call is prohibited

// ✅ good
const user = await client.getUser({ email: '[email protected]' }) // use API client to make sure user is created