การสร้างเวิร์กโฟลว์การทดสอบการรวมอย่างต่อเนื่องโดยใช้ GitHub Actions
เผยแพร่แล้ว: 2022-03-10เมื่อมีส่วนร่วมในโครงการบนแพลตฟอร์มการควบคุมเวอร์ชันเช่น GitHub และ Bitbucket หลักการคือมีสาขาหลักที่มีฐานโค้ดที่ใช้งานได้ นอกจากนี้ยังมีสาขาอื่นๆ ที่นักพัฒนาหลายคนสามารถทำงานกับสำเนาของ main เพื่อเพิ่มคุณสมบัติใหม่ แก้ไขข้อผิดพลาด และอื่นๆ เป็นเรื่องที่สมเหตุสมผลเพราะจะง่ายต่อการตรวจสอบผลกระทบที่การเปลี่ยนแปลงที่เข้ามาจะมีต่อโค้ดที่มีอยู่ หากมีข้อผิดพลาดใด ๆ ก็สามารถตรวจสอบและแก้ไขได้อย่างง่ายดายก่อนที่จะรวมการเปลี่ยนแปลงเข้ากับสาขาหลัก อาจใช้เวลานานในการค้นหาโค้ดทุกบรรทัดด้วยตนเองเพื่อค้นหาข้อผิดพลาดหรือจุดบกพร่อง แม้กระทั่งสำหรับโครงการขนาดเล็ก นั่นคือที่มาของการบูรณาการอย่างต่อเนื่อง
การบูรณาการอย่างต่อเนื่อง (CI) คืออะไร?
“การบูรณาการอย่างต่อเนื่อง (CI) เป็นแนวทางปฏิบัติในการรวมการเปลี่ยนแปลงโค้ดโดยอัตโนมัติจากผู้ร่วมให้ข้อมูลหลายรายในโครงการซอฟต์แวร์เดียว”
— Atlassian.com
แนวคิดทั่วไปเบื้องหลังการรวมอย่างต่อเนื่อง (CI) คือการทำให้แน่ใจว่าการเปลี่ยนแปลงที่เกิดขึ้นกับโครงการจะไม่ "ทำลายงานสร้าง" นั่นคือทำลายฐานรหัสที่มีอยู่ การใช้การรวมอย่างต่อเนื่องในโครงการของคุณ ขึ้นอยู่กับว่าคุณตั้งค่าเวิร์กโฟลว์ของคุณอย่างไร จะสร้างบิลด์เมื่อใดก็ตามที่ใครทำการเปลี่ยนแปลงกับที่เก็บ
ดังนั้น Build คืออะไร?
บิลด์ — ในบริบทนี้ — คือการรวบรวมซอร์สโค้ดให้อยู่ในรูปแบบที่ปฏิบัติการได้ หากประสบความสำเร็จ แสดงว่าการเปลี่ยนแปลงที่เข้ามาจะไม่ส่งผลเสียต่อ codebase และพร้อมดำเนินการ อย่างไรก็ตาม หากบิลด์ล้มเหลว การเปลี่ยนแปลงจะต้องได้รับการประเมินใหม่ นั่นคือเหตุผลที่แนะนำให้ทำการเปลี่ยนแปลงโปรเจ็กต์โดยการทำงานกับสำเนาของโปรเจ็กต์ในสาขาอื่นก่อนที่จะรวมเข้ากับโค้ดเบสหลัก ด้วยวิธีนี้ หากบิลด์พัง จะเป็นการง่ายกว่าที่จะหาว่าข้อผิดพลาดมาจากไหน และไม่ส่งผลต่อซอร์สโค้ดหลักของคุณด้วย
“ยิ่งจับจุดบกพร่องได้เร็วเท่าไหร่ ก็ยิ่งซ่อมได้ถูกกว่า”
— David Farley, การส่งมอบอย่างต่อเนื่อง: การเปิดตัวซอฟต์แวร์ที่เชื่อถือได้ผ่านการสร้าง การทดสอบ และการปรับใช้อัตโนมัติ
มีเครื่องมือหลายอย่างที่ช่วยในการสร้างการผสานรวมอย่างต่อเนื่องสำหรับโครงการของคุณ ซึ่งรวมถึง Jenkins, TravisCI, CircleCI, GitLab CI, GitHub Actions เป็นต้น สำหรับบทช่วยสอนนี้ ฉันจะใช้ GitHub Actions
GitHub Actions เพื่อการบูรณาการอย่างต่อเนื่อง
CI Actions เป็นฟีเจอร์ที่ค่อนข้างใหม่บน GitHub และช่วยให้สามารถสร้างเวิร์กโฟลว์ที่รันการสร้างและทดสอบโปรเจ็กต์ของคุณโดยอัตโนมัติ เวิร์กโฟลว์ประกอบด้วยงานอย่างน้อยหนึ่งงานที่สามารถเปิดใช้งานได้เมื่อมีเหตุการณ์เกิดขึ้น เหตุการณ์นี้อาจเป็นการพุชไปยังสาขาใดก็ได้ใน repo หรือการสร้างคำขอดึง ฉันจะอธิบายข้อกำหนดเหล่านี้โดยละเอียดเมื่อเราดำเนินการต่อไป
มาเริ่มกันเลย!
ข้อกำหนดเบื้องต้น
นี่คือบทช่วยสอนสำหรับผู้เริ่มต้น ดังนั้นฉันจะพูดถึง GitHub Actions CI ในระดับพื้นผิวเป็นส่วนใหญ่ ผู้อ่านควรคุ้นเคยกับการสร้าง Node JS REST API โดยใช้ฐานข้อมูล PostgreSQL, Sequelize ORM และการเขียนการทดสอบด้วย Mocha และ Chai
คุณควรติดตั้งสิ่งต่อไปนี้ในเครื่องของคุณด้วย:
- โหนดเจเอส,
- PostgreSQL,
- นพ.
- VSCode (หรือโปรแกรมแก้ไขและเทอร์มินัลที่คุณเลือก)
ฉันจะใช้ประโยชน์จาก REST API ที่ฉันสร้างแล้วชื่อ countries-info-api
เป็น api ธรรมดาๆ ที่ไม่มีการอนุญาตตามบทบาท (ในขณะที่เขียนบทช่วยสอนนี้) ซึ่งหมายความว่าทุกคนสามารถเพิ่ม ลบ และ/หรืออัปเดตรายละเอียดของประเทศได้ แต่ละประเทศจะมีรหัส (UUID ที่สร้างขึ้นโดยอัตโนมัติ) ชื่อ เมืองหลวง และประชากร เพื่อให้บรรลุสิ่งนี้ ฉันจึงใช้ Node js, express js framework และ Postgresql สำหรับฐานข้อมูล
ฉันจะอธิบายสั้น ๆ วิธีตั้งค่าเซิร์ฟเวอร์ ฐานข้อมูล ก่อนเริ่มเขียนการทดสอบความครอบคลุมของการทดสอบ และไฟล์เวิร์กโฟลว์สำหรับการผสานรวมอย่างต่อเนื่อง
คุณสามารถโคลน repo countries-info-api
เพื่อติดตามหรือสร้าง API ของคุณเอง
เทคโนโลยีที่ใช้ : Node Js, NPM (ตัวจัดการแพ็คเกจสำหรับ Javascript), ฐานข้อมูล Postgresql, ภาคต่อ ORM, Babel
การตั้งค่าเซิร์ฟเวอร์
ก่อนตั้งค่าเซิร์ฟเวอร์ ฉันได้ติดตั้งการพึ่งพาจาก npm
npm install express dotenv cors npm install --save-dev @babel/core @babel/cli @babel/preset-env nodemon
ฉันใช้กรอบงานด่วนและเขียนในรูปแบบ ES6 ดังนั้นฉันต้องใช้ Babeljs เพื่อคอมไพล์โค้ดของฉัน คุณสามารถอ่านเอกสารอย่างเป็นทางการเพื่อทราบข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานและวิธีกำหนดค่าสำหรับโครงการของคุณ Nodemon จะตรวจจับการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับโค้ดและรีสตาร์ทเซิร์ฟเวอร์โดยอัตโนมัติ
หมายเหตุ : แพ็คเกจ Npm ที่ติดตั้งโดยใช้ --save-dev
จำเป็นในระหว่างขั้นตอนการพัฒนาเท่านั้น และมองเห็นได้ภายใต้ devDependencies ในไฟล์ package.json
ฉันเพิ่มสิ่งต่อไปนี้ในไฟล์ index.js
ของฉัน:
import express from "express"; import bodyParser from "body-parser"; import cors from "cors"; import "dotenv/config"; const app = express(); const port = process.env.PORT; app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cors()); app.get("/", (req, res) => { res.send({message: "Welcome to the homepage!"}) }) app.listen(port, () => { console.log(`Server is running on ${port}...`) })
สิ่งนี้ตั้งค่า API ของเราให้ทำงานบนสิ่งใดก็ตามที่กำหนดให้กับตัวแปร PORT
ในไฟล์ . .env
นี่คือที่ที่เราจะประกาศตัวแปรที่เราไม่ต้องการให้ผู้อื่นเข้าถึงได้ง่าย dotenv
npm โหลดตัวแปรสภาพแวดล้อมของเราจาก . .env
ตอนนี้เมื่อฉันรัน npm run start
ในเทอร์มินัล ฉันได้รับสิ่งนี้:
อย่างที่คุณเห็น เซิร์ฟเวอร์ของเราเริ่มทำงานแล้ว เย้!
ลิงก์นี้ https://127.0.0.1:your_port_number/
ในเว็บเบราว์เซอร์ของคุณควรส่งคืนข้อความต้อนรับ นั่นคือตราบใดที่เซิร์ฟเวอร์ยังทำงานอยู่
ถัดไป ฐานข้อมูลและโมเดล
ฉันสร้างโมเดลประเทศโดยใช้ Sequelize และเชื่อมต่อกับฐานข้อมูล Postgres ของฉัน Sequelize เป็น ORM สำหรับ Nodejs ข้อได้เปรียบที่สำคัญคือช่วยเราประหยัดเวลาในการเขียนแบบสอบถาม SQL ดิบ
เนื่องจากเราใช้ Postgresql ฐานข้อมูลจึงสามารถสร้างได้โดยใช้บรรทัดคำสั่ง psql โดยใช้คำสั่ง CREATE DATABASE database_name
สิ่งนี้สามารถทำได้บนเทอร์มินัลของคุณ แต่ฉันชอบ PSQL Shell
ในไฟล์ env เราจะตั้งค่าสตริงการเชื่อมต่อของฐานข้อมูลของเรา ตามรูปแบบด้านล่างนี้
TEST_DATABASE_URL = postgres://<db_username>:<db_password>@127.0.0.1:5432/<database_name>
สำหรับแบบจำลองของฉัน ฉันทำตามบทช่วยสอนภาคต่อนี้ ง่ายต่อการติดตามและอธิบายทุกอย่างเกี่ยวกับการตั้งค่า Sequelize
ต่อไป ฉันจะเขียนการทดสอบสำหรับโมเดลที่ฉันเพิ่งสร้างและตั้งค่าความครอบคลุมบน Coverall
การเขียนแบบทดสอบและการรายงานผล
ทำไมต้องเขียนแบบทดสอบ? โดยส่วนตัวแล้ว ฉันเชื่อว่าการทดสอบการเขียนจะช่วยให้คุณในฐานะนักพัฒนาเข้าใจได้ดีขึ้นว่าซอฟต์แวร์ของคุณคาดว่าจะทำงานอย่างไรในมือของผู้ใช้ เนื่องจากเป็นกระบวนการระดมความคิด นอกจากนี้ยังช่วยให้คุณค้นพบข้อบกพร่องตรงเวลา
การทดสอบ:
มีวิธีการทดสอบซอฟต์แวร์ที่แตกต่างกัน อย่างไรก็ตาม สำหรับบทช่วยสอนนี้ ฉันใช้การทดสอบแบบหน่วยและแบบ end-to-end
ฉันเขียนการทดสอบโดยใช้กรอบการทดสอบ Mocha และไลบรารีการยืนยันของ Chai ฉันยังติดตั้ง sequelize-test-helpers
เพื่อช่วยทดสอบโมเดลที่ฉันสร้างขึ้นโดยใช้ sequelize.define
ครอบคลุมการทดสอบ:
ขอแนะนำให้ตรวจสอบความครอบคลุมในการทดสอบของคุณ เนื่องจากผลลัพธ์จะแสดงให้เห็นว่ากรณีทดสอบของเราครอบคลุมโค้ดจริงหรือไม่ และมีการใช้โค้ดมากน้อยเพียงใดเมื่อเราเรียกใช้กรณีทดสอบของเรา
ฉันใช้อิสตันบูล (เครื่องมือครอบคลุมการทดสอบ), nyc (ไคลเอนต์ CLI ของ Instabul) และ Coveralls
ตามเอกสาร อิสตันบูลใช้เครื่องมือโค้ด JavaScript ES5 และ ES2015+ พร้อมตัวนับบรรทัด เพื่อให้คุณสามารถติดตามว่าการทดสอบหน่วยของคุณใช้โค้ดเบสของคุณได้ดีเพียงใด
ในไฟล์ package.json
ของฉัน สคริปต์ทดสอบจะรันการทดสอบและสร้างรายงาน
{ "scripts": { "test": "nyc --reporter=lcov --reporter=text mocha -r @babel/register ./src/test/index.js" } }
ในกระบวนการนี้ จะสร้างโฟลเดอร์ .nyc_output
ที่มีข้อมูลความครอบคลุมดิบและโฟลเดอร์ coverage
ที่มีไฟล์รายงานความครอบคลุม ไฟล์ทั้งสองไม่จำเป็นใน repo ของฉัน ดังนั้นฉันจึงวางไว้ในไฟล์ .gitignore
ตอนนี้เราได้สร้างรายงานแล้ว เราต้องส่งไปยัง Coveralls สิ่งหนึ่งที่ยอดเยี่ยมเกี่ยวกับ Coveralls (และเครื่องมือความครอบคลุมอื่น ๆ ฉันถือว่า) เป็นวิธีที่รายงานความครอบคลุมการทดสอบของคุณ ความครอบคลุมจะแจกแจงเป็นไฟล์ตามแต่ละไฟล์ และคุณสามารถดูความครอบคลุมที่เกี่ยวข้อง บรรทัดที่ครอบคลุม และบรรทัดที่ไม่ได้รับ และสิ่งที่เปลี่ยนแปลงไปในการครอบคลุมของรุ่น
ในการเริ่มต้น ให้ติดตั้งแพ็คเกจ coveralls npm คุณต้องลงชื่อเข้าใช้ coveralls และเพิ่ม repo เข้าไปด้วย
จากนั้นตั้งค่า coveralls สำหรับโปรเจ็กต์ javascript ของคุณโดยสร้างไฟล์ coveralls.yml
ในไดเร็กทอรีรากของคุณ ไฟล์นี้จะเก็บ repo-token
ของคุณที่ได้รับจากส่วนการตั้งค่าสำหรับ repo ของคุณบนชุดคลุม
สคริปต์อื่นที่จำเป็นในไฟล์ package.json คือสคริปต์ครอบคลุม สคริปต์นี้จะมีประโยชน์เมื่อเราสร้างบิลด์ผ่านการดำเนินการ
{ "scripts": { "coverage": "nyc npm run test && nyc report --reporter=text-lcov --reporter=lcov | node ./node_modules/coveralls/bin/coveralls.js --verbose" } }
โดยพื้นฐานแล้ว จะทำการทดสอบ รับรายงาน และส่งไปยังชุดคลุมเพื่อการวิเคราะห์
มาถึงประเด็นหลักของบทช่วยสอนนี้
สร้างไฟล์เวิร์กโฟลว์ Node JS
ณ จุดนี้ เราได้ตั้งค่างานที่จำเป็นที่เราจะทำงานใน GitHub Action (สงสัยว่า “งาน” หมายถึงอะไร อ่านต่อไป)
GitHub ทำให้ง่ายต่อการสร้างไฟล์เวิร์กโฟลว์โดยจัดเตรียมเทมเพลตเริ่มต้น ตามที่เห็นในหน้าการดำเนินการ มีเทมเพลตเวิร์กโฟลว์หลายแบบที่ให้บริการตามวัตถุประสงค์ที่แตกต่างกัน สำหรับบทช่วยสอนนี้ เราจะใช้เวิร์กโฟลว์ Node.js (ซึ่งแนะนำ GitHub แล้ว)
คุณสามารถแก้ไขไฟล์ได้โดยตรงบน GitHub แต่ฉันจะสร้างไฟล์ใน repo ในพื้นที่ของฉันเอง โฟลเดอร์ .github/workflows
ที่มีไฟล์ node.js.yml
จะอยู่ในไดเร็กทอรีราก
ไฟล์นี้มีคำสั่งพื้นฐานอยู่แล้ว และความคิดเห็นแรกจะอธิบายสิ่งที่คำสั่งเหล่านั้นทำ
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
ฉันจะทำการเปลี่ยนแปลงบางอย่างเพื่อที่นอกเหนือจากความคิดเห็นข้างต้น มันยังครอบคลุมอยู่
ไฟล์ .node.js.yml
ของฉัน:
name: NodeJS CI on: ["push"] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run coverage - name: Coveralls uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_GIT_BRANCH: ${{ github.ref }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
สิ่งนี้หมายความว่า?
มาทำลายมันกันเถอะ
-
name
นี่จะเป็นชื่อของเวิร์กโฟลว์ของคุณ (NodeJS CI) หรืองาน (บิลด์) และ GitHub จะแสดงมันบนหน้าการดำเนินการของที่เก็บของคุณ -
on
นี่คือเหตุการณ์ที่ทริกเกอร์เวิร์กโฟลว์ บรรทัดนั้นในไฟล์ของฉันโดยพื้นฐานแล้วบอกให้ GitHub ทริกเกอร์เวิร์กโฟลว์ทุกครั้งที่มีการพุชไปยัง repo ของฉัน -
jobs
เวิร์กโฟลว์สามารถมีงานได้อย่างน้อยหนึ่งงาน และแต่ละงานรันในสภาพแวดล้อมที่ระบุโดยruns-on
ในตัวอย่างไฟล์ด้านบน มีเพียงงานเดียวที่รันบิลด์และรันความครอบคลุม และทำงานในสภาพแวดล้อมของ windows ฉันสามารถแยกมันเป็นสองงานที่แตกต่างกันเช่นนี้:
ไฟล์ Node.yml ที่อัปเดต
name: NodeJS CI on: [push] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run test coverage: name: Coveralls runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
-
env
ซึ่งประกอบด้วยตัวแปรสภาพแวดล้อมที่พร้อมใช้งานทั้งหมดหรือเฉพาะงานและขั้นตอนในเวิร์กโฟลว์ ในงานความครอบคลุม คุณจะเห็นว่าตัวแปรสภาพแวดล้อมถูก "ซ่อน" สามารถพบได้ในหน้าความลับของ repo ของคุณภายใต้การตั้งค่า -
steps
โดยพื้นฐานแล้วนี่คือรายการขั้นตอนที่ต้องทำเมื่อเรียกใช้งานนั้น - งาน
build
ทำหลายอย่าง:- มันใช้การดำเนินการเช็คเอาต์ (v2 หมายถึงเวอร์ชัน) ที่จะตรวจสอบที่เก็บของคุณอย่างแท้จริงเพื่อให้เวิร์กโฟลว์ของคุณสามารถเข้าถึงได้
- ใช้แอ็คชันเซ็ตอัพโหนดที่ตั้งค่าสภาพแวดล้อมของโหนดที่จะใช้
- มันรันการติดตั้ง สร้างและทดสอบสคริปต์ที่พบในไฟล์ package.json ของเรา
-
coverage
สิ่งนี้ใช้การกระทำของ coverallsapp ที่โพสต์ข้อมูลความครอบคลุม LCOV ของชุดทดสอบของคุณไปที่ coveralls.io สำหรับการวิเคราะห์
ขั้นแรกฉันกดไปที่สาขา feat-add-controllers-and-route
และลืมเพิ่ม .coveralls.yml
จาก Coveralls ไปยังไฟล์ .coveralls.yml ของฉัน ดังนั้นฉันจึงได้รับข้อผิดพลาดที่คุณเห็นในบรรทัดที่ 132
Bad response: 422 {"message":"Couldn't find a repository matching this job.","error":true}
เมื่อฉันเพิ่ม repo_token
ด์ของฉันสามารถทำงานสำเร็จ หากไม่มีโทเค็นนี้ ชุดคลุมจะไม่สามารถรายงานการวิเคราะห์ความครอบคลุมการทดสอบของฉันได้อย่างถูกต้อง สิ่งที่ดี GitHub Actions CI ของเราชี้ให้เห็นข้อผิดพลาดก่อนที่จะถูกส่งไปยังสาขาหลัก
หมายเหตุ: สิ่งเหล่านี้ถูกถ่ายก่อนที่ฉันจะแยกงานออกเป็นสองงาน นอกจากนี้ ฉันยังสามารถดูสรุปความครอบคลุมและข้อความแสดงข้อผิดพลาดบนเทอร์มินัลของฉันได้เพราะฉันเพิ่ม --verbose
flag ที่ส่วนท้ายของสคริปต์ความครอบคลุมของฉัน
บทสรุป
เราสามารถดูวิธีตั้งค่าการผสานรวมอย่างต่อเนื่องสำหรับโครงการของเรา และยังรวมความครอบคลุมการทดสอบโดยใช้การดำเนินการที่ GitHub จัดเตรียมให้ มีวิธีอื่นๆ มากมายที่สามารถปรับให้เข้ากับความต้องการของโครงการของคุณได้ แม้ว่า repo ตัวอย่างที่ใช้ในบทช่วยสอนนี้จะเป็นโปรเจ็กต์เล็กๆ น้อยๆ จริงๆ ก็ตาม คุณสามารถดูได้ว่าการผสานรวมอย่างต่อเนื่องมีความสำคัญเพียงใดในโครงการที่ใหญ่กว่า ตอนนี้งานของฉันประสบความสำเร็จ ฉันมั่นใจที่จะรวมสาขากับสาขาหลักของฉัน ฉันยังคงแนะนำให้คุณอ่านผลลัพธ์ของขั้นตอนต่างๆ หลังจากการรันทุกครั้งเพื่อดูว่าประสบความสำเร็จอย่างสมบูรณ์