Location>code7788 >text

Comprehensive Mastery of Jest: A Guide to Testing from Scratch (Part 1)

Popularity:116 ℃/2024-09-19 11:21:56

With the widespread use of JavaScript in front-end and back-end development, testing has become a critical part of ensuring code quality.

Why you need unit tests

In our development process, it is often necessary to define a number ofalgorithmic functionFor example, converting the data returned by the interface into the format required by the UI component. In order to verify the robustness of these algorithms, some developers may manually define a few input samples for initial verification, and once the verification passes, they will not be further investigated.

However, such an approach may bring somePotential problems. First, the boundary value case is often easily overlooked, leading to incomplete calibration and increasing the risk of system failure. Second, with the change and evolution of requirements, the algorithmic functions may need to be optimized and expanded. If the pre-checking work is not thorough enough and does not understand the specific scenarios covered by the existing functions, it may lead to the introduction of new problems in the subsequent modifications.

unit testThe above problem can be solved effectively. When defining the algorithmic function, synchronize the creation of unit test files, and the possible scenarios are listed one by one. If the unit test fails, the project will directly report errors when compiling, so that the problem can be identified and targeted in a timely manner. In addition, when new students join and need to extend the function, they not only need to add new test cases on the basis of the original unit test, but also to ensure the correctness of the new function, while guaranteeing the normal operation of the original function.

Customized Test Logic

Before we start using tools for unit testing, we can customize a tool function for testing.

For example, we have aadd function, expecting it to correctly compute the sum of two numbers, and verifying that the result is as expected. For example, we want to verify that2 + 3Is the result of the5 You can use theexpect(add(2, 3)).toBe(5) code like this to do so. To do this, we can define our ownexpect function so that it has something like Jest'sexpect Functions of the function

function add(a, b) { return a + b; }
function expect(result) {
  return {
    toBe(value) {
      if (result === value) {
        ("Validation succeeded"); } else {
      } else {
        throw new Error(`Execution error: ${result} ! == ${value}`); }
      }
    }, }
  }; }
}

// Example call
try {
  expect(add(2, 3)).toBe(5); // Output: "Validation successful"
  expect(add(2, 3)).toBe(6); // throws error
} catch (err) {
  (); // Output: "Execution error: 5 ! == 6"
}

To make our tests more descriptive and readable, we can further enhance our test logic. For example, we can add atest function that describes the purpose of the test and provides a more detailed error message if the test fails.

function test(description, fn) {
  fn(); fn(); fn(); fn(); fn()
    fn(); (`Test passed: ${description}`); fn()
    (`Test Passed: ${description}`); } catch (err) {
  } catch (err) {
    (`Test failed: ${description} - ${}`); }
  }
}
// Example call
test("Verify that 2 + 3 is equal to 5", () => {
  expect(add(2, 3)).toBe(5); }); }
}); test("Verify that 2 + 3 equals 5", () => { expect(add(2, 3)).toBe(5); }).
test("Verify that 2 + 3 is equal to 6", () => {
  expect(add(2, 3)).toBe(6);
}).

In this way, we modeled a simple test case where thetest respond in singingexpect function is similar to the one in Jest. However, our customized version is relatively rudimentary, lacking theJest Rich functionality provided.

Jest

Through the above examples, we can understand the basic ideas and methods of writing tests. However, in actual development, we need a more powerful and easy-to-use testing tool.Jest It is such a tool that not only provides a wealth ofmatchmaker(e.g., toBe, toEqual, etc.), and also support for theasynchronous testingMock FunctionsSnapshot test and other features.

pull intoJest dependency, we can directly use its built-intest respond in singingexpect function, thus greatly improving the efficiency and accuracy of the test.Jest The power of this is that it can help us to comprehensively cover various test scenarios and provide detailed error reports so that we can quickly locate and solve problems.

initialization

First of all, we pass thenpm install jest -D mountingJest dependency, and then execute thenpx jest --init. At this point, the command-line tool presents you with a series of interactive questions and answers asking you whether you want to add a script command called test to Jest, whether you want to use TypeScript as a configuration file, the environment in which your test cases will be executed, whether you want a code coverage test report, the compiler of the platform on which the test report will be generated, and whether you want to reset the state of the Mock function before each test case execution.

After completing all the questions and answers, Jest will modify the file and generates theConfiguration files. These configuration items will be relied upon when executing test cases.

We create a file and put the previous test code into it

function add(a, b) {
  return a + b;
}
test("beta (software) add function (math.)", () => {
  expect(add(2, 3)).toBe(5);
});

pass (a bill or inspection etc)npm run test Executing the Jest run command allows you to view detailed test information at the command line tool, including the status of which test case for which file, as well as a simple test coverage report.

In practical usage scenarios, theadd Functions are usually defined in the project file, and are accessed through the ES Modularity (export and import). By default, Jest does not support ES modular syntax, so we need to configure it with Babel.

First, install Babel and its core libraries and presets by running the following commands

npm install @babel/core @babel/preset-env --save-dev

Then, create thefile and define the configuration

 = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: {
          node: "current",
        },
      },
    ],
  ],
};

Next, place theadd function is moved to the file with theexport derive

// 
export function add(a, b) {
  return a + b;
}

Finally, use import in the file to import the

//
import { add } from './math';
test("beta (software) add function (math.)", () => {
  expect(add(2, 3)).toBe(5);
});

With these steps, you have completed the environment initialization for executing ES modular code with Jest.

matchmaker

One of the most commonly used features in Jest is the matcher. We touched on this earlier when we were doing testing with thetoBe This is a matcher, which is used to determine whether the values are equal or not. In addition to this, there are many other types of matchers.

equal in value

There are two types of matchers for determining the equality of values:toBe cap (a poem)toEqual. For basic data types (such as strings, numbers, and booleans), both are used with the same effect. But for reference types (such as objects and arrays), thetoBe Returns only if both references point to the same memory addresstrue

const user = { name: "alice" };
const info = { name: "alice" };

test("toEqual", () => {
  expect(info).toEqual(user); // passes, both have the same structure
}); test("toBe", () => { expect(info).
test("toBe", () => {
  expect(info).toBe(user); // fails, they have different references
}).
Is there a value

remaintoBeNulltoBeUndefined respond in singingtoBeDefined matcher to determine if the value is null, undefined, or defined, respectively.

test("toBeNull", () => {
  expect(null).toBeNull();
  expect(0).toBeNull(); // fail
  expect("hello").toBeNull(); // fail
  expect(undefined).toBeBull(); // fail
});

test("toBeUnDefined", () => {
  expect(null).toBeUndefined(); // fail
  expect(0).toBeUndefined(); // fail
  expect("hello").toBeUndefined(); // fail
  expect(undefined).toBeUndefined();
});

test("toBeDefined", () => {
  expect(null).toBeDefined();
  expect(0).toBeDefined();
  expect("hello").toBeDefined();
  expect(undefined).toBeDefined(); // fail
});
whether it is true or not

toBeTruthy is used to determine whether a value is true or not.toBeFalsy is used to determine if the value is false.not Used for taking the opposite.

test("toBeTruthy", () => {
  expect(null).toBeTruthy(); // fails
  expect(0).toBeTruthy(); // not passed
  expect(1).toBeTruthy(); // fail
  expect("").toBeTruthy(); // fail
  expect("hello").toBeTruthy(); // fail
  expect(undefined).toBeTruthy(); // fails
});
test("toBeFalsy", () => {
  expect(null).toBeFalsy();
  expect(0).toBeFalsy();
  expect(1).toBeFalsy(); // not passed
  expect("").toBeFalsy();
  expect("hello").toBeFalsy(); // fails
  expect(undefined).toBeFalsy(); // Not passed.
}).
test("not", () => {
  expect(null). ();
  expect("hello"). (); // not passed
});
numerical comparison

toBeGreaterThan is used to determine whether it is greater than a certain value.toBeLessThan is used to determine whether it is less than a certain value.toBeGreaterThanOrEqual is used to determine whether it is greater than or equal to a certain value.toBeCloseTo Used to determine if it is close to a certain value (difference < 0.005).

test("toBeGreaterThan", () => {
  expect(9).toBeGreaterThan(5);
  expect(5).toBeGreaterThan(5); // fail
  expect(1).toBeGreaterThan(5); // fail
});

test("toBeLessThan", () => {
  expect(9).toBeLessThan(5); // fail
  expect(5).toBeLessThan(5); // fail
  expect(1).toBeLessThan(5);
});

test("toBeGreaterThanOrEqual", () => {
  expect(9).toBeGreaterThanOrEqual(5);
  expect(5).toBeGreaterThanOrEqual(5);
  expect(1).toBeGreaterThanOrEqual(5); // fail
});

test("toBeCloseTo", () => {
  expect(0.1 + 0.2).toBeCloseTo(0.3);
  expect(1 + 2).toBeCloseTo(3);
  expect(0.1 + 0.2).toBeCloseTo(0.4); // fail
});
string related

toMatch Used to determine whether the string contains the specified substring, partially contains can be.

test("toMatch", () => {
  expect("alice").toMatch("alice"); // passed
  expect("alice").toMatch("alice"); // passed
  expect("alice").toMatch("al"); // passed
}).
Array Related

toContain is used to determine whether an array contains the specified element, similar to the JavaScriptincludes Methods.

test("toContain", () => {
  expect(['banana', 'apple', 'orange']).toContain("apple");
  expect(['banana', 'apple', 'orange']).toContain("app"); // fail
});
error-related

toThrow Used to determine whether a function throws an exception, and can specify the specifics of the exception thrown.

test("toThrow", () => {
  const throwNewErrorFunc = () => {
    throw new TypeError("this is a new error");
  };
  expect(throwNewErrorFunc).toThrow();
  expect(throwNewErrorFunc).toThrow("new error");
  expect(throwNewErrorFunc).toThrow("TypeError"); // fail
});

These are the common matchers for each type.

command-line tool

exist Medium Configurationscript command, which enables the. The file automatically executes test cases in real time as they are modified.

"scripts": {
   "jest": "jest --watchAll"
},

From the command line, you will see the results of the current test case execution in real time. Meanwhile, Jest also provides some shortcut configurations by pressing thew key to see exactly what commands are available.

There are several main types:

f Mode
Among all test cases, only the last failed test case is executed. Even if the content of other test cases is modified, they will not be executed.

o Mode
Execute only modified test cases. This feature needs to be used in conjunction with theGit based on the changes made to the Git repository relative to the last time it was used. This pattern can also be achieved by configuring the script command, which is:

"script": {
"test": "jest --watch"
}

p-mode
When using the --watchAll When you modify the code of a file, all test cases are executed. Enter thep After the mode, you can enter the file namematchersFileIf you modify any file at this point, it will only look for the file that contains thematchersFile file and execute it.

t-mode
Enter a test case name that matches the first argument of the test function. If the match is successful, the test case is executed.

q-mode
Exit real-time code detection.

With different commands, you can test test cases in a more targeted way.

hook function

In Jest, the describe function is used to put together a set of related test cases (tests) to form a descriptive test block. It takes two arguments: the first is a string that describes the subject of the test block; the second is a function that contains a set of test cases.

Even if the describe function is not explicitly defined, each test file is wrapped with a layer of describe by default at the outermost level.

Within each of the blocks that describe consists of, there are a number of hook functions that are used throughout the test case. These hooks are mainly used to prepare the test case before execution or to clean it up afterwards.

Common Hook Functions
  • The beforeAll function is executed once before the start of a describe block.
  • The afterAll function is executed once at the end of a describe block.
  • The beforeEach function is executed before each test case
  • afterEach is executed after each test case.
sample code (computing)

The following sample code shows how to use these hook functions:

describe("Testing for values", () => {
  beforeAll(() => {
    ("beforeAll");
  });
  afterAll(() => {
    ("afterAll");
  }).
  beforeEach(() => {
    ("beforeEach");
  }).
  describe("toBeNull", () => {
    beforeAll(() => {
      ("toBeNull beforeAll");
    });
    afterAll(() => {
      ("toBeNull afterAll");
    });
    beforeEach(() => {
      ("toBeNull beforeEach");
    });
    test("toBeNull", () => {
      expect(null).toBeNull(); }); test("toBeNull", () => {
    });
  });
});
output sequence

When the above test case is run, the sequence of output is as follows:

beforeAll
toBeNull beforeAll
beforeEach
toBeNull beforeEach
toBeNull afterAll
afterAll

By using these hook functions, you can better manage the lifecycle of your test cases, ensuring that each test starts with a clean state and cleaning up any side effects generated at the end of the test.

In this test guide, we introduce theBackground of Jest, how to initialize a project, common matcher syntax, hook functionsThe next post will continue to delve into Jest's advanced features, including The next article will continue to delve into Jest's advanced features, includingMock functions, handling of asynchronous requests, mocking of Mock requests, mocking of classes and timers, use of snapshots. With these techniques, we will be able to write and maintain test cases more efficiently, especially when dealing with complex asynchronous logic and external dependencies.