เมื่อโปรเจกต์เริ่มซับซ้อนขึ้น
การมั่นใจว่าโค้ดมีคุณภาพดี และผ่านการทดสอบอย่างครอบคลุมก็ยิ่งสำคัญขึ้นตาม
ทั้งหมดนี้เริ่มจากคำถามง่ายๆ:

  • เทสที่เขียน…ครอบคลุมพอหรือยัง?
  • มี code smell แอบซ่อนอยู่ไหม?
  • โค้ดที่ขึ้น production แล้ว…ยังมี bug หลุดไปอยู่หรือเปล่า?

คำตอบแบบ “น่าจะโอเคมั้ง” เริ่มฟังดูไม่น่าไว้ใจเท่าไหร่
และนั่นแหละ… ถึงเวลาที่เราต้องพึ่งเครื่องมือวิเคราะห์โค้ดอย่าง SonarQube
เพื่อช่วยจับจุดที่เรามองไม่เห็น และทำให้คุณภาพโค้ดดีขึ้นอย่างจริงจัง

ทำไมต้อง SonarQube?

SonarQube เป็นเครื่องมือวิเคราะห์คุณภาพของซอร์สโค้ด ที่สามารถรายงานได้ทั้ง

  • Code Smells – โค้ดที่เขียนได้ แต่อาจทำให้เข้าใจยากหรือดูแลยากในระยะยาว
  • Bugs – จุดที่อาจทำให้โค้ดทำงานผิดพลาดหรือเกิด error
  • Duplications – โค้ดที่ซ้ำกันในหลายจุด ทำให้เปลี่ยนทีต้องแก้หลายที่
  • Security Issues – โค้ดที่อาจเปิดช่องให้เกิดช่องโหว่ด้านความปลอดภัย
  • Test Coverage – สัดส่วนของโค้ดที่ถูกครอบคลุมโดย unit tests

Simple API ด้วย Node.js พร้อม Unit Test ด้วย Jest

เพื่อให้เห็นภาพการใช้งาน SonarQube ชัดเจนยิ่งขึ้น
เราจะยกตัวอย่างโปรเจกต์ Node.js เล็กๆ ที่มี API และเขียน unit test ครอบคลุมไว้ด้วย Jest

โครงสร้างโปรเจกต์

simple-api/
├── app.js                         # โค้ด API หลัก
├── package.json                   # รายละเอียดโปรเจกต์และสคริปต์
├── sonar-project.properties       # คอนฟิกสำหรับ SonarQube
├── docker-compose.yml             # รัน SonarQube และ Sonar Scanner ด้วย Docker
├── coverage/                      # โฟลเดอร์ที่ Jest สร้างหลังรัน coverage
│   └── lcov.info                  # รายงาน test coverage สำหรับ SonarQube
└── __tests__/                     # โฟลเดอร์เก็บ unit test
    └── app.test.js                # ตัวอย่าง unit test สำหรับ API

ไฟล์ package.json

{
  "name": "simple-api",
  "version": "1.0.0",
  "description": "Simple API for testing",
  "scripts": {
    "start": "node app.js",
    "test": "jest --coverage"
  },
  "dependencies": {
    "express": "5.1.0"
  },
  "devDependencies": {
    "jest": "30.0.2",
    "supertest": "7.1.1"
  }
}

ไฟล์ app.js

const express = require('express');
const app = express();

// Mock users data
const users = [
  { id: 1, name: 'Teera' },
  { id: 2, name: 'Tee' }
];

// GET /users
app.get('/users', (req, res) => {
  res.json(users);
});

const PORT = process.env.PORT || 3000;
if (require.main === module) {
  app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
}

module.exports = app;

ไฟล์ __tests__/app.test.js

const request = require('supertest');
const app = require('../app');

describe('GET /users', () => {
  it('should return list of users', async () => {
    const res = await request(app).get('/users');
    expect(res.statusCode).toBe(200);
    expect(res.body).toEqual([
      { id: 1, name: 'Teera' },
      { id: 2, name: 'Tee' }
    ]);
  });
});

รัน Unit Test พร้อม Coverage Report

npm test

ผลลัพธ์ควรขึ้นประมาณนี้

> [email protected] test
> jest --coverage      

 PASS  __tests__/app.test.js
  GET /users
    √ should return list of users (132 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |      80 |       75 |      50 |   88.88 |                   
 app.js   |      80 |       75 |      50 |   88.88 | 17                
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.754 s
Ran all test suites.

และจะมีโฟลเดอร์ coverage/ ถูกสร้างขึ้นโดย Jest
โดยเฉพาะไฟล์ coverage/lcov.info ที่เราจะใช้กับ SonarQube ในขั้นถัดไป

ติดตั้งด้วย Docker

เพื่อความสะดวก เราจะใช้ Docker Compose ในการรันทั้ง SonarQube และ Sonar Scanner แบบแยก service

ไฟล์ docker-compose.yml

services:
  sonarqube:
    image: sonarqube:community
    container_name: sonarqube
    ports:
      - "9000:9000"
    environment:
      - SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
    volumes:
      - sonarqube_data:/opt/sonarqube/data

  sonar-scanner:
    image: sonarsource/sonar-scanner-cli
    container_name: sonar-scanner
    volumes:
      - .:/usr/src
    working_dir: /usr/src
    command: sonar-scanner

volumes:
  sonarqube_data:

เริ่มจากการรัน SonarQube server ขึ้นมาก่อน

docker compose up -d sonarqube

แล้วเข้า http://localhost:9000
login ด้วย admin / admin (ระบบจะให้เปลี่ยนรหัสผ่าน)

หมายเหตุ: อาจต้องรอสักครู่ (ประมาณ 30–60 วินาที) ให้ SonarQube ทำงานจนพร้อม หากเปิดหน้าเว็บแล้วยังโหลดไม่ขึ้น ให้รีเฟรชอีกครั้งหลังรอสักพัก

สร้างโปรเจกต์ใน SonarQube

  1. Create Project
  2. เลือก Manually
  3. ใส่ชื่อโปรเจกต์และ Project Key (เช่น simple-api)
  4. เลือก Locally สำหรับวิธีการวิเคราะห์
  5. กด Generate Token แล้ว คัดลอก token ไว้ (ใช้กับ sonar-scanner)
  6. กด Continue

สร้างไฟล์ sonar-project.properties

sonar.projectKey=simple-api
sonar.sources=.
sonar.host.url=http://sonarqube:9000
sonar.token=sqp_3c88208399bfcd77f8b21f50a0a70975aeb70214

sonar.tests=__tests__/
sonar.test.inclusions=**/*.test.js,**/*.spec.js
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.exclusions=coverage/**,node_modules/**
  • sonar.projectKey : ใช้ตรงกับชื่อโปรเจกต์ใน SonarQube
  • sonar.host.url : URL ของ SonarQube (ถ้าใช้ใน Docker Compose ชี้ไปที่ sonarqube:9000)
  • sonar.token : Token ที่ได้จากขั้นตอนก่อนหน้า
  • sonar.sources : โฟลเดอร์ที่มี source code
  • sonar.tests : โฟลเดอร์ที่เก็บ test
  • lcov.reportPaths : ตำแหน่งไฟล์ coverage ที่ Jest สร้าง
  • sonar.exclusions : ไฟล์/โฟลเดอร์ที่ไม่ต้องสแกน

เริ่มสแกนโค้ดด้วย SonarQube

เมื่อทุกอย่างพร้อมแล้ว ทั้งโปรเจกต์, unit test, coverage และไฟล์ sonar-project.properties
เราสามารถสั่งสแกนได้ทันที

docker compose run --rm sonar-scanner

ถ้าทุกอย่างถูกต้อง จะเห็นผลลัพธ์ในหน้า SonarQube Dashboard

  • รายงาน Test Coverage จาก Jest
  • ตรวจเจอ Code Smells, Bugs และ Duplications (ถ้ามี)
  • สถานะ Quality Gate บอกว่าโปรเจกต์ผ่านเกณฑ์คุณภาพหรือไม่

สรุป

  • ตั้งค่า SonarQube แบบ Local ด้วย Docker
  • ใช้โปรเจกต์ Node.js ที่มี unit test และ coverage report
  • สร้างโปรเจกต์และ token ใน SonarQube
  • เขียนไฟล์ sonar-project.properties เพื่อกำหนดการสแกน
  • รันคำสั่ง sonar-scanner เพื่อสแกนโค้ด
  • ดูรายงานผลผ่านเว็บ SonarQube เพื่อประเมินคุณภาพโค้ด

แม้ว่าเราอาจไม่สามารถเขียนโค้ดที่สมบูรณ์แบบได้ทุกครั้ง แต่การรู้ว่าจุดไหนในโค้ดที่ควรปรับปรุง
จะช่วยให้เราก้าวหน้าและพัฒนาโปรเจกต์ได้อย่างยั่งยืนในระยะยาว 🙂