Stimulus — это легковесный JavaScript-фреймворк, который позволяет связывать поведение с элементами DOM через контроллеры. В реальных приложениях контроллеры часто взаимодействуют с асинхронными процессами: сетевыми запросами, таймерами, WebSocket и другими событиями. Тестирование таких сценариев требует особого подхода, чтобы корректно проверять результаты и состояние интерфейса после завершения асинхронных операций.
В Stimulus контроллер может содержать методы, выполняющие асинхронные действия. Пример типичного сценария:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["output"]
async fetchData() {
this.outputTarget.textContent = "Загрузка..."
const response = await fetch("/api/data")
const data = await response.json()
this.outputTarget.textContent = data.message
}
}
Ключевые моменты:
async/await).Для тестирования Stimulus-контроллеров используют Jest, Mocha, или другие фреймворки, поддерживающие асинхронные тесты. Важные аспекты:
Тестирование метода fetchData может выглядеть следующим
образом:
import { Application } from "@hotwired/stimulus"
import DataController from "./data_controller"
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ message: "Привет, мир!" })
})
)
describe("DataController", () => {
let application, element
beforeEach(() => {
application = Application.start()
element = document.createElement("div")
element.innerHTML = `<span data-target="data.output"></span>`
application.register("data", DataController)
document.body.appendChild(element)
})
afterEach(() => {
application.stop()
document.body.innerHTML = ""
jest.clearAllMocks()
})
test("обновляет содержимое после fetch", async () => {
const controller = application.getControllerForElementAndIdentifier(element, "data")
await controller.fetchData()
expect(element.querySelector("[data-target='data.output']").textContent).toBe("Привет, мир!")
expect(fetch).toHaveBeenCalledWith("/api/data")
})
})
Особенности:
await гарантирует, что тест дождется
завершения асинхронной операции.fetch предотвращает реальные
HTTP-запросы.Контроллеры часто используют setTimeout или
setInterval. Jest позволяет управлять временем с помощью
fake timers:
jest.useFakeTimers()
test("показывает сообщение после задержки", () => {
const element = document.createElement("div")
element.innerHTML = `<span data-target="data.output"></span>`
const controller = new DataController(element, {})
controller.delayedMessage()
expect(element.querySelector("[data-target='data.output']").textContent).toBe("")
jest.advanceTimersByTime(1000)
expect(element.querySelector("[data-target='data.output']").textContent).toBe("Готово")
})
Преимущества:
Иногда необходимо убедиться, что определенный метод контроллера
вызван после асинхронного события. Jest предоставляет возможность
создавать шпионы (jest.spyOn):
test("вызывает метод обработки данных после fetch", async () => {
const controller = application.getControllerForElementAndIdentifier(element, "data")
const spy = jest.spyOn(controller, "processData")
await controller.fetchData()
expect(spy).toHaveBeenCalledWith({ message: "Привет, мир!" })
})
Это полезно для сложных контроллеров, где результат асинхронного действия передается в другие методы.
await или возвращать промис в
тестах.Асинхронное тестирование в Stimulus позволяет точно моделировать пользовательские сценарии и гарантировать стабильность поведения интерфейса при взаимодействии с внешними ресурсами. Оно обеспечивает уверенность, что контроллер корректно обновляет DOM и вызывает необходимые методы в нужный момент.