Jest предоставляет мощные возможности для тестирования, включая поддержку пользовательских матчеров. Такие матчеры позволяют расширить функциональность стандартных методов проверки, добавив специфические проверки, которые могут быть полезны для конкретных нужд проекта. Однако, чтобы эффективно использовать их с TypeScript, важно правильно типизировать эти матчеры, обеспечив их корректную работу и поддержку автодополнения.
Для начала рассмотрим, как создать простой пользовательский матч. Допустим, нам нужно написать матч для проверки, что строка содержит определённое подстрочное значение.
expect.extend({
toContainSubstring(received, argument) {
const pass = received.includes(argument);
if (pass) {
return {
message: () => `expected ${received} not to contain ${argument}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to contain ${argument}`,
pass: false,
};
}
},
});
Этот матч проверяет, что строка received содержит
подстроку argument. Однако если в проекте используется
TypeScript, нам нужно дополнительно типизировать этот матчер, чтобы
гарантировать правильную работу с типами.
Когда создаётся пользовательский матч, необходимо описать типы для передаваемых аргументов и возвращаемого значения. Jest уже включает типы для стандартных матчеров, но для пользовательских матчеров их необходимо задать вручную.
Каждый матч в Jest — это функция, которая принимает два аргумента:
фактическое значение и ожидаемое значение. В случае пользовательского
матчера важно правильно типизировать эти параметры. Для этого можно
использовать типы, которые предоставляет Jest, такие как
MatcherContext и Matchers из пакета
@jest/globals.
Пример типизации матчера:
import { MatcherContext, Matchers } from '@jest/globals';
declare global {
namespace jest {
interface Matchers<R> {
toContainSubstring(expected: string): R;
}
}
}
expect.extend({
toContainSubstring(received: string, argument: string) {
const pass = received.includes(argument);
if (pass) {
return {
message: () => `expected ${received} not to contain ${argument}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to contain ${argument}`,
pass: false,
};
}
},
});
Здесь мы добавили интерфейс Matchers, расширив его на
метод toContainSubstring. Это позволяет TypeScript
корректно распознавать наш матчер в тестах.
Функция матчера должна возвращать объект, который содержит два
ключевых поля: pass (булевое значение, которое указывает,
прошёл ли тест) и message (функция, которая возвращает
сообщение об ошибке). Важно правильно типизировать этот объект.
interface MatcherResult {
pass: boolean;
message: () => string;
}
В функции нашего матчера этот результат будет возвращаться через объект:
toContainSubstring(received: string, argument: string): MatcherResult {
const pass = received.includes(argument);
if (pass) {
return {
message: () => `expected ${received} not to contain ${argument}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to contain ${argument}`,
pass: false,
};
}
}
Такой подход гарантирует, что TypeScript будет точно знать, какой тип возвращаемого значения ожидать.
В Jest можно расширить не только функциональность, но и типизацию стандартных матчеров. Например, добавление новых аргументов в матчеры или создание комбинированных матчеров.
Пример комбинированного матчера, который проверяет и тип, и содержимое строки:
declare global {
namespace jest {
interface Matchers<R> {
toBeStringAndContainSubstring(expected: string): R;
}
}
}
expect.extend({
toBeStringAndContainSubstring(received: unknown, argument: string) {
if (typeof received !== 'string') {
return {
message: () => `expected a string, but received ${typeof received}`,
pass: false,
};
}
const pass = received.includes(argument);
if (pass) {
return {
message: () => `expected ${received} not to contain ${argument}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to contain ${argument}`,
pass: false,
};
}
},
});
В данном примере мы проверяем тип received, а затем
проверяем, содержит ли он подстроку. Важно, чтобы в случае ошибки тип
был корректно указан, а также правильно сформулировано сообщение об
ошибке.
После того как пользовательский матч был написан и типизирован, его можно использовать в тестах, как обычный матч. Пример использования:
test('string contains substring', () => {
expect('Hello, world!').toContainSubstring('world');
});
test('checks string type and content', () => {
expect('Hello, world!').toBeStringAndContainSubstring('world');
});
TypeScript будет проверять типы и предоставлять автодополнение в редакторах, что улучшает опыт разработки и снижает количество ошибок.
pass и message. Это помогает избежать
неожиданных ошибок и гарантировать корректную работу тестов.Правильная типизация пользовательских матчеров в Jest не только улучшает поддержку автодополнения, но и повышает надёжность тестов. Использование TypeScript с Jest позволяет создавать сложные, но типобезопасные расширения для проверки специфичных условий, что делает код тестов более чётким и предсказуемым.