蒼時弦也
蒼時弦也
資深軟體工程師
發表於

在 Rails 的前後端分離 - Cucumber 的文件測試法

在前面的實作中我們將前端跟後端分別獨立進行開發,這是很常見的一種方式。然而在 Rails 7 的生態系裡面,透過 jsbundling-rails 可以很輕鬆的整合在一起,接下來我們會使用類似基礎的 ViteRuby 把前端的 Cucumber 測試搬移到 Rails 上重現一次性整合完畢的實作。

安裝 Vite Ruby

要安裝 ViteRuby 非常簡單,開發團隊已經準備好了 vite_rails 可以直接套用到 Rails 專案中,關於 Rails 中設定 Cucumber 等開發環境的部分,可以直接使用我預先製作好的樣板 rails-developer-foundation-template 來進行開發。

首先,我們基於樣板建立新的專案,然後運行以下命令加入 ViteRails 套件。

1bundle add vite_rails
2bundle exec vite install

執行完畢後會自動將原有的 jsbundling-rails 做一些更新,我們只需要驗證 bin/vite dev 是否可以正確運行即可。

安裝 Vue

Vite 的定位是簡化前端開發的工具,因此預設不會有 Vue 的環境存在,我們需要在 ViteRuby 生成的基礎下手動將 Vue 加入到專案中。

所需的設定可以參考前面純前端版本的實作複製設定,我們在這邊只會做必要的調整。

運行以下命令把 Vue 和 Vite 的外掛安裝進去。

1yarn add vue vite-plugin-ruby

接下來修改自動生成的 vite.config.ts 加入 Vue 的外掛,大致上會呈現這個樣子。

 1import { defineConfig } from 'vite'
 2import RubyPlugin from 'vite-plugin-ruby'
 3import Vue from '@vitejs/plugin-vue'
 4
 5export default defineConfig({
 6  plugins: [
 7    RubyPlugin(),
 8    Vue(),
 9  ],
10})

這樣一來我們就可以在專案中使用 Vue 來進行開發,然而目前會把 jsbundling-rails 和 ViteRuby 的設定混在一起,因此我們還需要清理一下 Rails 的 Layout。

修改 app/views/layout/application.html.erb 把 jsbundling 相關的部分清除掉,留下 ViteRuby 的引用,並且加入 Vue 要附加的 Div 元素。

 1<!DOCTYPE html>
 2<html>
 3  <head>
 4    <title>DeveloperFoundation</title>
 5    <meta name="viewport" content="width=device-width,initial-scale=1">
 6    <%= csrf_meta_tags %>
 7    <%= csp_meta_tag %>
 8
 9    <%= vite_client_tag %>
10    <%= vite_typescript_tag 'application', "data-turbo-track": "reload", defer: true %>
11  </head>
12
13  <body>
14    <div id="app">
15      <%= yield %>
16    </div>
17  </body>
18</html>

這裡只加入最低限度的 JavaScript 實際專案中使用可以根據情況調整,並且我們預設所有頁面都會被 Vue 替換掉,因此將 <%= yield %><div> 標籤包覆起來。

因為預設採用 TypeScript 的關係,別忘記將 app/javascript/entrypoints/application.js 的副檔名替換為 .ts

用 Cucumber 驗證環境

最後,我們要驗證我們可以用 Cucumber 來對前端做測試,加入 features/products.feature 和以下內容,先用於確認 Vue 正確的被載入。

1#language:zh-TW
2@javascript
3功能: 商品列表
4  場景: 當開啟網站時,可以看到 Hello, ViteRuby
5 開啟網站
6    那麼 可以看見 "Hello, ViteRuby"

這裡會發現我們額外加入了 @javascript 的標籤,這會讓 Cucumber 意識到需要呼叫 Capybara 使用真實的瀏覽器運行去模擬,關於對應的設定可以參考樣板中 features/support/env.rb 的內容,以及 cucumber-rails 的文件。

接著加入 feature/step_definitions/common.rb 來定義步驟,雖然我們可以沿用 .feature 文件,然而在不同語言跟環境中,會需要用對應的語言重新實作步驟定義。

1# frozen_string_literal: true
2
3When('開啟網站') do
4  visit root_path
5end
6
7Then('可以看見 {string}') do |text|
8  expect(page).to have_text(text)
9end

這邊使用 Capybara 的匹配器(Matcher)來檢查畫面上有出現的文字。

接下來我們修改 app/javascript/entrypoints/application.ts 的內容從預設的樣板,改為初始化 Vue 的實作。

1import { createApp } from 'vue'
2import App from '@/App.vue'
3
4const app = createApp(App)
5app.mount('#app')

再將 app/javascript/App.vue 補到專案裡面,寫上 Hello, ViteRuby 來配合我們的第一個測試。

1<template>
2  <div>Hello, ViteRuby</div>
3</template>

最後補上一個空的 Controller 和 View 讓我們可以將畫面渲染出來。

1Rails.application.routes.draw do
2  root 'stores#index'
3end
1# app/controllers/stores_controller.rb
2
3class StoresController < ApplicationController
4end
1mkdir -p app/views/stores
2touch app/views/stores/index.html.erb

接下來運行 bundle exec cucumber 確認是否能正常通過測試,如果遇到 JavaScript 檔案不存在的話,可以運行 ./bin/dev 或者 bundle exec rake assets:precompile 來生成,在 Rails 專案樣板中預設使用 Chrome Headless 模式,如果是自己安裝 Cucumber 的話要可以看到跳出瀏覽器開啟畫面的樣子。

接下來,就要把前端的實作轉移到 Rails 上來重現相同的功能。