Mastering Jest's beforeEach Hook for Efficient Test Setup
This challenge focuses on understanding and implementing Jest's beforeEach hook in TypeScript. This hook is crucial for ensuring that your tests are independent and repeatable by executing specific setup logic before each individual test within a describe block. Mastering beforeEach will significantly improve your test suite's organization and reduce code duplication.
Problem Description
You are tasked with writing a series of Jest tests for a simple Counter class. This Counter class has methods to increment, decrement, and reset its internal value. To ensure each test starts with a fresh Counter instance, you need to utilize the beforeEach hook to create and initialize a new Counter object before every test runs.
What needs to be achieved:
- Define a
Counterclass withincrement,decrement, andresetmethods. - Write Jest tests for the
Counterclass. - Implement a
beforeEachhook within adescribeblock to instantiate a newCounterobject before each test.
Key requirements:
- The
Counterclass should maintain an internalvalueproperty. - The
incrementmethod should increase thevalueby 1. - The
decrementmethod should decrease thevalueby 1. - The
resetmethod should set thevalueback to 0. - Each test should operate on a separate instance of the
Counterclass.
Expected behavior:
- When
incrementis called, the counter's value should increase. - When
decrementis called, the counter's value should decrease. - When
resetis called, the counter's value should become 0. - Tests should not interfere with each other. For example, a test that increments the counter should not affect the starting value of the next test.
Edge cases to consider:
- Decrementing the counter when its value is already 0. The behavior should be to simply remain at 0.
Examples
Let's assume we have a Counter class defined as follows (you will need to implement this):
class Counter {
private value: number = 0;
increment(): void {
this.value++;
}
decrement(): void {
if (this.value > 0) {
this.value--;
}
}
reset(): void {
this.value = 0;
}
getValue(): number {
return this.value;
}
}
Test Scenario 1: Initial Value
Test Code Snippet:
// Assuming Counter class is imported and beforeEach is set up
describe('Counter', () => {
let counter: Counter; // Declare counter here
beforeEach(() => {
counter = new Counter(); // Instantiate in beforeEach
});
test('should initialize with a value of 0', () => {
expect(counter.getValue()).toBe(0);
});
});
Explanation:
The beforeEach hook creates a new Counter instance, setting its initial value to 0. The test then asserts that getValue() returns 0.
Test Scenario 2: Incrementing
Test Code Snippet:
// Assuming Counter class is imported and beforeEach is set up
describe('Counter', () => {
let counter: Counter;
beforeEach(() => {
counter = new Counter();
});
test('should increment the value', () => {
counter.increment();
expect(counter.getValue()).toBe(1);
});
test('should increment the value multiple times', () => {
counter.increment();
counter.increment();
expect(counter.getValue()).toBe(2);
});
});
Explanation:
Each test block in this describe block will have a fresh Counter instance due to beforeEach. The tests verify that calling increment correctly updates the value.
Test Scenario 3: Decrementing and Resetting
Test Code Snippet:
// Assuming Counter class is imported and beforeEach is set up
describe('Counter', () => {
let counter: Counter;
beforeEach(() => {
counter = new Counter();
});
test('should decrement the value', () => {
counter.increment(); // Start with 1
counter.decrement();
expect(counter.getValue()).toBe(0);
});
test('should not decrement below zero', () => {
counter.decrement(); // Start with 0
expect(counter.getValue()).toBe(0);
});
test('should reset the value to 0', () => {
counter.increment();
counter.increment();
counter.reset();
expect(counter.getValue()).toBe(0);
});
});
Explanation:
These tests demonstrate decrementing behavior, including the edge case of decrementing from zero, and the functionality of the reset method. Crucially, beforeEach ensures that each of these operations starts with a fresh counter.
Constraints
- Your solution should be written in TypeScript.
- You must use Jest as the testing framework.
- You must implement the
beforeEachhook to manage theCounterinstance. - The
Counterclass should be a simple, non-async class.
Notes
- The goal is to demonstrate your understanding of how
beforeEachhelps maintain test isolation and reduces setup boilerplate. - Think about how you would declare the variable that will hold your
Counterinstance within thedescribeblock. - The
beforeEachhook can be used for more complex setup tasks than just instantiation, such as mocking dependencies or setting up API stubs. This challenge focuses on the fundamental use case.