作者: paul

AI 左移對軟體開發的變革 ,C#後端開發為例

AI 左移對軟體開發的變革 ,C#後端開發為例


🧠 AI 左移:從 Chat 到 Commit 的開發革命

傳統 IDE 時代,重構工具、語法補全與靜態分析已經幫助我們加速開發。但現在,我們正站在另一個斷代點──AI 開發模式的橫空出世

從最早的「問答式 AI」產生 code snippet,逐漸演進到「動作外包」、到可以在 CLI 中自然對話,甚至進一步進入 Cursor 等具備 Project Context、Agent System、MCP(Multi-Agent Command Protocol) 的多工 AI 工具戰國時代。


🤖 AI 自己發 PR、自己 Code Review 的時代?

當我看到這支影片 [AI 左移影片連結],我才真正意識到,這並不是空想:

AI 不只會寫程式,它可以從需求分析、Mock UI、拆解任務、估算工時、產出規格,直到 PR 審查形成一個完整開發閉環。

而這樣的流程,正在顛覆我們原本對「軟體開發分工」的理解與作法。


🪛 為什麼 AI 要往前移動(AI 左移)?

過去,我們在開發流程中只在實作階段使用 AI,而忽略了一件更關鍵的事:

⚠️ 人類的瓶頸從來不在 code,而在「對問題的理解」與「開發過程中資訊的流失」。

透過 CLI + Project Context + Prompt 工程技巧,AI 可以開始參與「定義問題」的過程,進入專案需求前期:

  • ✍️ 需求口述 → 語音備忘 → 文字轉錄
  • 📄 轉為 markdown 文件(需求規格.md)
  • 🧠 透過 Prompt 進行 extended thinking / chain-of-thought 鏈式思考
  • 讓 AI 主動復述需求,對齊理解,減少溝通誤差
  • 🧩 開始進行 Mock、WBS 拆解、工時預估

🛠️ AI 開發流的實作節奏(從需求到派工)

以下是我目前思考的實驗流程(你也可以試試看):

flowchart TD
    A[需求口述/備忘錄] --> B[生成 需求規格.md]
    B --> C[AI協助產出 todo.md(拆工+估時)]
    C --> D[AI產出 SD規格文件.md(技術設計)]
    D --> E[開 PR,開始進行協作]
    E --> F[Reviewer 產出 refinement.md]
    F --> G[對應 JIRA 拆單派工]

補充:

  • 每份 .md 檔案皆納入版控,AI 分析時可以讀取上下文。
  • .todo.md 為階段性任務清單,支持迭代審查與 prompt 精化。
  • .refinement.md 可成為後續 QA 或重構依據。

🧩 提示詞工程才是核心技能(不是工程本身)

與 AI 合作的關鍵不是「讓它幫我寫 code」,而是「讓它理解我要什麼」。

關鍵提示詞技巧:

  • 說清楚你的技術要求框架範圍邏輯驗收條件
  • 用正向語言描述需求(「要怎樣」而不是「不要怎樣」)
  • 補充範圍與限制(避免硬編碼、可維護性、可擴展性)
  • 在 prompt 中註明:「請不要只為了測試通過而實作」

🔁 Git + AI 迴圈:低風險高效率的實驗場

AI Left Shift 的特點,就是快速試錯,但這不代表無序。你可以透過下列策略控管風險:

  • 使用 /init 指令快速建立專案理解基底(產出掃描報告)
  • /clear, Esc, /reset 等指令控管 prompt 紀錄與狀態清理
  • 利用 Git 分支搭配 PR 討論實施「嘴炮即代碼」的思維
  • 不一定要選 highlight code,文字描述即可驅動 AI 修改提案

🔁 分享一下,近期我對於C#後端如何搭配Cli或Cursor(接下來簡稱AI)

C#傳統IDE(我用Rider,以下以Rider為例)的強項其實是Debug、Testing跟 Trace Code以及人為的重構工具

AI進來以後,其實工具的演化 非常快,目前看來確實 AI Editor以 Cursor還是算相當成熟的工具,當然這邊指的不是像Claude AI的model,指的是AI寫扣的能力 而是ui與cli的平衡生態

圖示(IDE 的好處,在於對人類的友善性)

以下針對後端軟體開發一樣分成3階段,常態下,我會對我的AI Editor設定好Plan, Coding , Designing的Rule, Guide line的md檔案給他,然後同時開起AI Editor(Cursor)與Rider後(Mac上兩指左右滑就可以快速切換),開始進行feature開發

第一階段

描述需求,並聊透,分析完整,進行評估與限制(注意prompt與context的上下文工程)
跟AI 在IDE Chat完需求後,進入下一階段

第二階段

請他在AI Editor以TDD的角度開發測試
這時候切換到Rider跑測試,編譯不過或紅燈,就把錯誤貼回chat請他修正
這段其實是AI的短版,搭配人類協助測試與檢核編譯(但我相信很快cursor或Cli就會自動檢查這類的東西了)

第三階段

有測試後,再堆疊案例,這時透過AI重構意義就有了保障,直到完成需求並驗收通過

以上我實測了以後,其實在第二階段是AI容易撞牆的地方,偶爾還是給出無法編譯的程式,有時還是會給出不如預期的命名或設計,但我認為撞牆大部分都還是你給的指令 不夠明確,或是所謂預期工程不足(要什麼正向表達)。若我們小步小步的移動來讓 AI進行開發的話,可以透過人類的補充資訊,更精準的取得修正的方向,AI獲得更多 context後,都確實可以做的更好。


📌 總結:讓 AI 更早參與,才能真正減少浪費

從需求定義、Mock、估時、PR、派工、Refinement,AI 都可以參與。

AI 有短版,但人的「記憶力、注意力、思考速度」也有極限。

將 AI 左移,不是為了讓它代替人,而是讓它幫助人把最難搞的前中期開發流程梳理清楚減少返工提升開發信心與節奏

這不僅是開發方式的變革,更是我們與 AI 協作型態的一場進化。

👉 需求規格.md → todo.md → SD設計文件 → refinement.md → jira 任務派工

這一套,會是我認為未來專案管理 + AI 工程協作的骨架

後記 :

AI的短版其實會複製人類的短版,若人類給的prompt不夠具體,AI就會一樣會亂猜跟幻覺(可能跟你老闆一樣),不夠具體的定義 不只是prompt的問題,Context跟Scope的限縮也有關係,這就是cli跟ide帶來的優勢,而不是只有chatgpt的對話記錄而已;我試過1500行的cs程式,請他重構符合規範(有具體的要求 ) vs 請他只針對cs中某個測試 function(具體名稱)去進行 明確的重構(可以在prmpt講清楚或是有參考檔都可以),那麼後者效率會好很多。這證明了,人類專注記憶力有限的缺點,其實在AI也有,必竟現在的很多專家模型都是混搭在workflow上去建構agent。跟他工作也要把他當成教育現場,有耐心的去tune他跟讓他小步確認與對齊(可以試試犯錯)來修正他的產出!

再舉個例,很多人問AI的時候,就會把100萬個字 或整個專案 當context丟給他,再問他的一個抽象的問題,例如整理所有的重點。可想而知,AI只能猜你想要什麼,再加上性能跟算力的限制,就會給你一個被限制也無法達到你需求的結果,憤而覺得AI難用 。這時其實就是拆解context跟task,降低ai要猜測你的成本其實會對生成式的結果品質會有飛躍式的提升。另外多試試一些deep think,ultrathink等指示詞,有機會讓 ai進行換model運作。也有機會改善輸出品質,但前提一樣是要給對context跟 問對問題!!

Who is ZhouShen

Who is ZhouShen

第一次看到他也是從2014年初登場中國好聲音第3季,一直以來,我都會多多少少觀注一下素人選秀節目 ,從台灣的星光大道,超偶,到後來大放異彩的中國選秀節目。其實那時聽到這首歌的時候,對他的聲音覺得特別,雖然有歡顏與貝加爾湖畔的比賽作品,但我其實就僅僅是有印象而已

初登場: 歡顏

第二首-貝加爾湖畔 (被那英淘汰的名場面)

真的對他驚豔的就是這個大魚美聲合唱版,以前有聽音樂劇跟世界三大男高音,其實我對這種美聲唱法就是有愛好~,所以我就開始 密切關注他,這時的他也默默已經有一些作品了。(動畫-大魚主題曲)

中國好聲音幫唱版本 ,唱到陳奕迅 都稱讚

因為周深是烏克蘭音樂學院畢業的,以致於後來看到多首他的美聲的作品時 ,他的技巧也愈發成熟

以這兩首為例(2019~2020年左右)

歌劇魅影 – Think of Me

貓- Memory

他把握住了每一個的舞台,因為他都做好了最萬全的準備,展現了他絕佳的控制力 ,嗓音條件。

恰恰符合我對跨界美聲的審美,同時又具備有獨特性,在那時看來,根本就是一個鑽石(他勵志的故事又是另一段故事了)

他曾經說過,他特別珍惜並全力準備每一個表演的機會,因為他唯有這樣,才有機會有”下一個舞台”

有2020~2024年 ,幾乎可以看到他不斷的出現在各大晚會與典禮,也有無數的歌讓他唱,甚至一度戲稱勞動楷模

也是因為這樣,他開始被很多人聽到

香港明星們對周深的評價

那時因為幾個音綜開始嶄露頭角,我們的歌與他一起參演的周華健也 把對他的讚賞,帶到台灣,

連知名電台主持人光禹都開始挖起他的礦(看看下面的歌單就知道這個坑的深度),做了寶藏週記 一做也好幾年了

而其實這幾年 來周深的成長,從YOUTUBE的作品、能見度,都可以看的出來,他本身也很努力,他曾經說過 ,剛開始很多人對他的評價是∶「原來周深是一個男孩子,唱是唱的不錯,但若是做為一個女孩子,就顯著普通了」,他覺得很多人因為這樣 否定了他在音樂上下的努力。

然而他不畏酸言酸語,繼續憑實力與求新求變的勇氣,讓自己始終能保持,這是一個特別是他從不自信的轉變過來的,看著他就像自己的孩子一樣成長起來。

跟貝加爾湖畔 原唱 李健老師合作了一個即興舞台

出道十年 專訪,可以見得,他是一個心思非常細膩且正向的一個人

早期辛酸的經歷

如今的維基百科寫道 : 周深是一位備受讚譽的中國男歌手,以其清亮溫柔的嗓音和多變的演唱風格著稱。自2014年參加《中國好聲音》第三季正式出道以來,他在音樂領域取得了眾多成就,以下是他的一些重要榮譽與貢獻:

🎤 音樂成就與代表作品
* 代表歌曲:《大魚》、《燈火裡的中國》、《小美滿》、《和光同塵》、《花開忘憂》等,這些作品展現了他在流行、古風、電子等多種音樂風格中的出色表現。
* 專輯發行:截至目前,周深已發行兩張錄音室專輯:《深的深》和《反深代詞》,並發表超過200首單曲。

銷售成績與認證

  • 首日銷售額:專輯發行首日即創下2500萬元人民幣的銷售額。

  • 首週銷售額:首週銷售額突破3000萬元人民幣。

  • 累計銷量:截至2024年12月,專輯累計銷量超過167萬張,銷售額突破5900萬元人民幣,成為2024年中國內地專輯銷量冠軍。 

  • 平台認證:《反深代詞》是2024年QQ音樂平台上首張也是唯一一張獲得“史詩唱片”認證的專輯。


🌍 國際成就

  • IFPI全球專輯銷量榜:該專輯在2024年IFPI全球專輯銷量榜中排名第11位,是當年唯一上榜的華語專輯。

  • 單曲認證:專輯中的4首歌曲獲得雙白金認證,1首獲得白金認證,7首獲得黃金認證,累計認證單位達1250萬。

以他的成長期,以我認識他的部分,我將 時間軸分類如下
* 前期(中國好聲音2014+首張專輯,少數翻唱) 2014~2018
* 中期 (2019~2022)
主要是靠默默累績曝光(OST大量累積超過100首,其中包含成名曲大魚)
另一個部分是音綜累積 作品期( 各種 合作舞台 ,翻唱創作 – 留下yt作品)
註: 他在跑男也很活躍,所以在中國很出圈~
* 現在 (2023~now)
大鳴大放期(各種音樂獎、邀約、晚會演出,國內外巡迴)
* 以上年限就我印象,不用太在意
另外以音樂風格作品,又可于分成
中國風(曲調本身京有中國風,或加入戲腔),因為本著仙嗓天賦,通常效果拉滿,方文山都認證
一般流行(翻唱 or ost現代劇) 就看歌順不順耳就好

跨界(融合美聲唱法) -> 我特別有興趣的

以出處來分的話,以下歌單大部分就是很單純的分法,沒有排名,大部分絕得要嘛是順耳,不然就是覺得 是值得一再品味的現場或錄音室 作品~

一看到JJ找他當佳賓,也算原了周深偶像的一個夢想,他們同台飆唱 真的滿感動的~因為這也是看到他被前輩認可了
JJ + 周深 《裹着心的光》 (JJ北京演唱會)

各大音綜時期

蒙面唱將猜猜猜

《雪落下的声音》

雪落下的聲音

《我真的受伤了》

杨丞琳、周深
《她说》

《我是真的爱你》李宗盛

聲入人心 我很愛聽音樂劇,這完美 發揮了他美聲
莎拉布萊曼 《Time to say goodbye》

貓- Memory

歌劇魅影 – Think of Me

Version 2

周深 王晰 月彎彎 二重唱

合作舞台 (他唱合音超強,超like他的二創加分作品)
天賜的聲音- 周深-張韶涵 – 一路生花

周深GAI周延合唱《玫瑰少年》 (原唱: 五月天or蔡依林)

我們的歌
周深 李克勤 – 張國榮 (追 )

周深 李克勤 – 《月半小夜曲》

周深 李克勤 – 《天下有情人《我是真的爱你》》

時光音樂會- 梁詠琪 – 花火

時光音樂會- 阿杜 – Andy

時光音樂會- 周杰倫 – 聽見下雨的聲音

時光音樂會- 月光(原唱 : 胡彥斌 轉音一絕)

原唱王菲 – 如願

五月天- 好好

起風了

《歌手·当打之年》Singer 2020
歌手 – 達拉崩吧 (獵奇)

無問 – 原唱:毛不易

《自己按门铃自己听》

《大鱼》

声生不息·家年华- 周深 汪蘇瀧 愛的供養 (原: 楊冪)

声生不息·家年华- 周深 黃綺珊 歲月 (原: 王菲)

声生不息·家年华- 周深 宋亞軒 桃花諾 (原: 鄧紫琪)

声生不息·家年华- 周深 陳楚生 逆光(原 : 孫燕姿)

舞台 2023 朴仔範、王嘉爾、Ella供他清唱大眠名場面

音樂緣計劃(新歌很多)
沉默的羔羊

合作版
薛之谦&周深《只字不提》

周深&周笔畅《孤独音乐家》

二次元作品+翻唱 (他出圈前,在中國二次元界已經爆紅)
《Rubia》——《崩壞3rd》印象曲

《Unravel》 – 原唱 TK from 凛として時雨

迪士尼合作作品
《奇遇乐章》

星願 I’m a Star

20201224周深Charlie Zhou Shen 九语版《Let It Go》

OST電視原聲帶(古風劇)
《蒼蘭訣》-《餘情》

慶餘年 2-借過一下

《願得一心人》(電視劇鶴唳華亭主題曲)

《願》(電視劇錦衣之下主題曲)

《若夢》(電視劇 夢醒長安 主題曲)

陳情令《荒城渡》

周深 + 鄭雲龍 -《曇花一現雨及時》(電視劇三千鴉殺主題曲)

《明月傳說》(電視劇 風起霓裳 主題曲)

Sword and Fairy《祈今朝》 | 《共鸣》

【長歌行 The Long Ballad OST電視劇片尾主題曲】

《相擁不放》(無損音樂連歌詞)(《白月梵星》影視劇主題曲)

一晌 (電視劇《狐妖小紅娘月紅篇 Fox Spirit Matchmaker: Red-Moon Pact》片尾曲)

曼陀 (《淮水竹亭》影视剧主题曲/片尾曲

以上…太多了,遺珠之憾應該還有超過10首

OST電影原聲帶

《水形物語-奧斯卡金像獎最佳影片》(電影水形物語同名推廣曲)

《問花》(電影 白蛇2:青蛇劫起 主題曲)

張藝謀 – 《懸崖之上》(電影 懸崖之上 同名主題曲)

《來不及勇敢》(電影昨日青空青春告白曲)

《小美滿》【熱辣滾燙 YOLO OST 電影熱辣陪伴曲】

電影《解密》中文版同名主題曲

《流浪地球2》定义主题曲《人是_》

OST電視原聲帶(現代劇主題區)

《消散人潮》【北上 Northward OST 電視劇片尾曲

《雲邊的風箏》(又名《我們的王鶯鶯》

《风吹过的晨曦》(玫瑰的故事情感主题曲)

《幸福的故事》幸福裡

人世間-光字片

生活總該迎著光亮 (電視劇《喬家的兒女》主題曲)

過客 (《以愛為營》電視劇主題曲)

全英文歌
周深《避難所 Sanctuary》(英語單曲)

《My Only》(電視劇 開端 片尾主題曲)

還有滿多現場 作品是英文歌

戲腔風格
周杰倫 – 《蘭亭序》

《易燃易爆炸》

光亮 – 記錄片或實境節目 主題區

手遊作品: 江湖3系列 第一部: 江湖緣起

第二部: 江湖覓知音

第三部: 《我,江湖》《一梦江湖》

藝術歌曲 或合作舞台聯合國表演 – 《和平頌》

其他現場or翻唱 作品

鐵達尼號 My heart will go on (席琳迪翁)

雪落下的聲音(神翻唱)

《 問世間情是何物》 ( 詞:元好問 • 金朝)

Unstoppable (原唱: SIA)

Fire

身騎白馬(原唱: 徐佳瑩)

《天堂島之歌》

周深萨顶顶《左手指月》

Zhou Shen/周深《與你同在》【い つ も 何 度 で も】[《千與千尋》主題曲日文版]

自創曲-虛構 現場晚會首唱

齐豫+周深 合唱《欢颜》 2024-2025跨年

《我用所有报答爱》 – 譚盾 交響樂團

《浮光》

《我以渺小爱你》

《花开忘忧》

《卧龙吟》

首張專輯 周深 Charlie Zhou【深的深】 全分享(10首歌)

時隔七年,第二張個人專輯 反深代詞https://youtube.com/playlist?list=PLrlBqwsy1XpTJSap7VwatCdz6Fh0eUIYM&si=4rb5CSCdYfR12OEq

.net core 應用程式 在AppService進行dotnet dump

.net core 應用程式 在AppService進行dotnet dump

當應用程式有High Memory 或是High Cpu的現象時,若希望對應用程式進行Runtime的分析,以前在IIS上常常進行的是在工作管理員對w3wp進行傾印,移到雲端又是Container形式後,跟以往 在windows的經驗不大一樣,不得其門而入,以下記錄一下作法

在Container For AppService的環境下要能進行Dump,會有以下 步驟

Container安裝dotnet-dump 的tool

# 前略...
# Open port 2222 for SSH access
EXPOSE 80 2222

# dotnet
RUN apk add icu-libs tzdata
RUN apk --update add libgdiplus ttf-dejavu
RUN apk add --no-cache bash curl icu-libs libc6-compat \
    && curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
    && bash /tmp/dotnet-install.sh --channel 8.0 --install-dir /usr/share/dotnet 

# 安裝 dotnet-dump
RUN /usr/share/dotnet/dotnet tool install -g dotnet-dump \
    && ln -s /root/.dotnet/tools/dotnet-dump /usr/bin/dotnet-dump \
    && dotnet --info

# 後略...

確認Docker Build成功

通常你的webapp有多個 instance,請先設定環境變數增加以下 設定為true

WEBSITES_ENABLE_APP_SERVICE_STORAGE

這個設定是讓 多個instance的/home目錄共用,後續可以透過kudu的newui進行FileManager下載Dump檔案

透過進階工具連到開發工具(不是上面的SSH,這個外層的SSH應該主要是連到Container Host的主機,要連到指定的Instance的話,可以進到Kudu去指定Instance)

接著ssh連進去 確認Container內dotnet-dump版本

/home/LogFiles# dotnet-dump --version
9.0.553101+5b61d34de04d6100e6003415f7d7e9c4b971afd4

確認指定ok後,查詢process id

ps aux | grep dotnet
16 root 5h23 dotnet /app/example.dll
10891 root 0:00 grep dotnet

進行dump collect,注意,此步驟在linux container進行時,並不會中斷process或重啟(這跟我們之前w3wp.exe的經驗不太一樣,不過或許還是會間接造成伺服器比較忙錄,所以可以離峰進行)

dotnet-dump collect  -p [process-id]  --type Full  --diag

Writing full to /home/LogFiles/core_20250211_010126
Complete

成功後

透過kudu 的FileManager就可以找到檔案 進行下載分析~~

Reference :

https://techcommunity.microsoft.com/blog/appsonazureblog/how-to-collect-net-core-dump-on-linux-web-app/2260713

支援TestContainer資料庫整合自動化測試的Clean Architecture專案模版-2024年版總結

支援TestContainer資料庫整合自動化測試的Clean Architecture專案模版-2024年版總結

在前幾份工作透過課程學過單元測試與重構,工作領域就是較多的傳統三層式架構(ex: Presentation-Service-Repository),直到前一份接觸到了實務的Clean Architecture方案開始(約莫三年),發現了C#開發的新天地,終於有一種先前課程單元測試與重構、極速開發學到的技巧有機會應用到工作上的感覺。

因此最近在多個產品領域逐步試驗抽換,有些產品是從0-1,我覺得相對容易,只要開發Pattern形成,其實風格跟文化就會自動形成。

大概的要素如下:

  1. Clean Architecture的專案模版(EFCore .NET+ MediatR-CQRS)
  2. 自動化 IntegrationTest + UnitTest

Clean Architecture的專案模板(EFCore .NET+ MediatR-CQRS)

傳統 N 層架構與 Clean Architecture 的差異

最大的不同在於 依賴注入(DI) 的引入後,Infrastructure 層參考 Application 層,而 API 層 則參考 Application 層MediatR 物件。MediatR 提供了一種 標準化的行為,讓使用層(Client 或 API)可以透過抽象方法進行呼叫(類似 Invoke Action)。透過 IRequest + IRequestHandler,請求會標準化地傳遞至 Application 層 執行。這樣的設計提高了 可測試性,因為大部分的 業務邏輯內聚在 Application 層,不像傳統架構可能將部分邏輯散落在 Web API 等層級中。當然,開發者仍然可以 不遵循原則,把邏輯寫得分散,這就需要有意識地去避免這種情況。在這樣的架構下,Web API 的 Controller 只需專注於與 IHttpContext 交互,或處理 Request-Level 事務,例如: Mapping Command / Query 物件

到了 Application 層,應該只剩下 業務邏輯,不應與 HttpContext 這類原生或底層物件耦合。這樣能大幅提高 Application 層的可測試性。反之Infrastructure 層 應該完全封裝對外部系統(如 Redis、資料庫(DB)、其他第三方套件)的實作,外部專案只能透過 Interface 操作,確保解耦與可維護性。

Clean Architecture 中,DbContext 屬於 Infrastructure 層,而 Entities 則歸屬 Domain 層。實際上,Entity 本質上就是資料庫表的物件對應。如果 Schema 設計符合 Use Case,資料操作起來就會 更直覺,就像在處理業務邏輯,而不只是冷冰冰的 SQL 操作(如 Insert、Update、Delete)。

使用了EntityFramework後,雖然我們可以透過 DI 綁定 IDBContext,讓任意使用端操作資料庫。這樣的做法 雖然方便,但不建議長期使用。或許在 開發初期,這種方式有助於 自動化測試,因為 TDD 產生的測試可以直接驗證資料庫行為。然而,當系統進入 重構階段,應將 IDBContext 介面Application 層 提取到 Service 層,這樣的調整 將會非常輕鬆,同時也能讓架構更符合 Clean Architecture 原則

自動化 IntegrationTest + UnitTest

單元測試的 3A 原則(Arrange, Act, Assert),以及 Mock、Fake、Stub 等技巧固然重要,但這裡想強調的是一個 觀念上的轉變。我認為 單元測試不僅僅是函式層級的測試,而是 只要測試對象可以完全控制,就能算是單元測試。如果一個函式綁定了資料庫操作,是否還能進行單元測試?我認為可以。只要能在測試前準備好資料庫,並在測試結束時清除資料,那麼就能夠 更真實地模擬函式的行為

這樣的做法避免了繁瑣的 Mock 設定,因此 TestContainer 應運而生。過去,準備一個測試用資料庫相對麻煩,不僅需要在本機安裝,還要在 CI/CD 環境中對應不同的資料庫實例。但隨著 Docker 的出現,以及 微軟支援 Docker 版 SQL Server,這個問題得到了有效解決。因此,因為「資料庫」難以”測試”已不應再成為藉口

曾有人問我,許多業務邏輯因為過度依賴資料庫,而導致難以進行良好的單元測試。常見的解法是 將資料庫操作封裝進 IRepository,但接下來的問題是:我們是否應該對 IRepository 進行 Mock?我的結論是:看需求與團隊接受程度。如果我們可以 自動建置資料庫,且 IRepository 主要對接資料庫,那麼就應該讓測試驗證實際的資料庫操作。如果不測試,我認為測試是不完整的,這樣的測試結果也不太令人安心。至於 IProxy 介接外部 API 的部分,我認為這屬於 異質系統,通常交由外部團隊維護,因此建議 Mock 掉

TestContainer 與 Integration Test

在了解 TestContainer 的概念後,我們的目標是讓 每次測試(包含 CI Pipeline)都能進行 Integration Test。因此,我建議採取以下做法:

1. 透過 Docker 自動建立資料庫

2. 自動建置 Schema(可參考 Repository 中 IntegrationTest 的 BeforeRun)

3. 自動升級資料庫版本,確保 Application 依賴的資料庫狀態正確

如此一來,撰寫每個 Test Case 時,數據層都能確保已被測試。

最後,我建議搭配 Database.Reset 套件(目前支援 SQL ServerPostgreSQL),以便快速重置資料庫,確保每次測試環境的一致性。這樣一來,我們就能實踐 「整合單元測試」(Integration Unit Test)。

維護成本與 Schema 同步

維護成本主要來自於 Schema 的同步,這部分可由 DBA 維護,或 團隊自行定義同步方式。這裡沒有標準做法,重點是讓流程 舒適、易於維護,才能長期執行下去。

這些做法在我們團隊已經試驗超過半年,整體來說 可行,團隊成員也已經習慣撰寫測試,並且包含 數據庫驗證測試。目前,我正協助 其他產品團隊 導入 自動化數據庫測試流,調整適合他們的方案。

當然,這個過程難以避免 一定程度的重構,而且不同方案各有風險。由於大多數系統缺乏良好的測試保護,導致改造充滿挑戰。然而,透過 測試技巧,可以逐步 抽離 Interface(這是我認為最重要的一步)。

整體來看,主要的挑戰在於 改造策略與推進進度,但這 並非技術或測試不可行的問題。這部分有機會的話,我會在後續進一步分享。

Clean Architecture 的一般化方案

為了讓 微軟 C# Application 能夠 快速理解並導入 Clean Architecture,我對這套架構進行了一定程度的 一般化整理,並已經在 GitHub 上建立範本。這不僅是個紀錄,也能作為 範本,方便有需要的團隊 直接導入或參考

連結如下: https://github.com/pin0513/ECAppForCleanArchitecture

todo: 會持續將 MediatR的案例整合進去哦

Nodejs並支持 React的CMS解決方案(todo study)

Nodejs並支持 React的CMS解決方案(todo study)

在您提到的选项中,以下的 CMS 可以通过 Docker Compose 构建,并使用 MariaDB 进行数据库连接,类似于 WordPress 的部署方式:

1. Strapi

Docker 支持:Strapi 提供了官方的 Docker 镜像,并有示例供参考。

MariaDB 支持:Strapi 默认支持 MariaDB,您可以在配置中指定。

部署示例

以下是一个使用 Docker Compose 部署 Strapi 和 MariaDB 的示例:

version: '3'

services:

  strapi:

    image: strapi/strapi

    ports:

      - '1337:1337'

    environment:

      DATABASE_CLIENT: mysql

      DATABASE_HOST: db

      DATABASE_PORT: 3306

      DATABASE_NAME: strapi

      DATABASE_USERNAME: root

      DATABASE_PASSWORD: example

    depends_on:

      - db

  db:

    image: mariadb:latest

    environment:

      MYSQL_ROOT_PASSWORD: example

      MYSQL_DATABASE: strapi

    volumes:

      - db_data:/var/lib/mysql

volumes:

  db_data:

使用步骤

1. 将上述内容保存为 docker-compose.yml 文件。

2. 在终端中运行 docker-compose up -d 启动服务。

3. 访问 http://localhost:1337,按照提示完成 Strapi 的初始设置。

2. KeystoneJS

Docker 支持:虽然 KeystoneJS 没有官方的 Docker 镜像,但您可以自己编写 Dockerfile,或者使用社区提供的镜像。

MariaDB 支持:通过 Prisma,KeystoneJS 可以连接到 MariaDB。

部署示例

由于需要自定义配置,建议参考以下步骤:

1. 编写 Dockerfile:创建一个包含您的 KeystoneJS 应用的 Dockerfile。

2. 编写 docker-compose.yml:配置 KeystoneJS 服务和 MariaDB 服务。

3. 运行:使用 docker-compose up -d 启动。

3. Payload CMS

Docker 支持:Payload CMS 提供了 Docker 部署指南。

MariaDB 支持:支持 MySQL 兼容的数据库,包括 MariaDB。

部署示例

version: '3'

services:

  payload:

    image: payloadcms/payload

    ports:

      - '3000:3000'

    environment:

      PAYLOAD_CONFIG_PATH: ./payload.config.js

      DATABASE_URL: mysql://root:example@db:3306/payload

    volumes:

      - ./:/usr/src/app

    depends_on:

      - db

  db:

    image: mariadb:latest

    environment:

      MYSQL_ROOT_PASSWORD: example

      MYSQL_DATABASE: payload

    volumes:

      - db_data:/var/lib/mysql

volumes:

  db_data:

4. Webiny

Docker 支持:Webiny 可以使用 Docker 部署,但主要设计为无服务器架构。

MariaDB 支持:需要进行额外的配置,可能不如其他选项直接。

推荐方案

鉴于您的需求,Strapi 是最直接和方便的选择。它提供了官方的 Docker 镜像,默认支持 MariaDB,而且社区资源丰富。

参考资料

Strapi 官方文档Strapi – Getting Started with Docker

Payload CMS Docker 指南Payload CMS – Docker Deployment

注意事项

环境变量:确保在 docker-compose.yml 中正确配置环境变量,如数据库用户名、密码和数据库名称。

数据持久化:使用 Docker 卷(volumes)来持久化数据库数据,防止数据在容器重启时丢失。

端口映射:确认服务的端口映射,避免与本地其他服务冲突。

我覺得wordpress的優勢在於 plugin很多,很多金流都有套件可以用…看來要找時間來試一下這些架站是否可以用來取代wordpress了

2024 WordPress自動架站心得-DockerCompose with AI

2024 WordPress自動架站心得-DockerCompose with AI

最近一個朋友想架官網,我第一個想到的還是wordpress

雖然以技術而言,php感覺已經不是主流(主流是GO, Python….etc),但以能做到CMS網站的解決方案而言,wordpress看起來還是滿強勢,重點是前後台就一站solution包好了,不用去想前後端分離什麼的 ,所以還是試著先搭配AI去安裝看看吧~

第一步,申請Gmail,為你雲端服務商申請有一個信箱 可以註冊

Azure跟 AWS都有免費 12個月,一定大小的size的vm規格可以申請,其實大同小異,這邊我選了熟悉的GCP

第二步 登入gmail後,直接申請 GCP,這邊其實 因為 都是google的服務,所以很順的申請完成!

他一開始會有一個MyFirstProject,一時間無法改名這個Project,所以我就立即創建一個新的Project,免費額度內好像可以建立共12個Project。然後申請服務的時候,會需要信用卡,但他說不會刷額度,在300美元額度內,你不啟用,都可以試用他們的服務,就算額度用完,他也不會跟你直接扣款。

第三步,開啟Compute-Engine,建立一個VM,這時我只有選一個core2, 8g ram的機器,其他都幾乎沒改,連地域都放在us(其實可以挑一下),一直下一步,等待一會兒就可以看到他背景建置好了!

第四步

透過 webssh連進去(這個比起當年還要建立ssh金鑰,上傳 Server才能用 iterm連線 的時代已經方便超級多了!

第五步

問Chatgpt要怎麼wordpress架站,首先先安裝Docker

第一個prompt如下

"Linux instance-20241202-162827 6.1.0-27-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.115-1 (2024-11-01) x86_64

我要怎麼安裝docker "

他回應我如下,我就不全列了,反正AI都會教好教滿,若你貼錯了 把錯誤訊息再丟回去問他,他也會試圖幫你解決

第六步: 安裝Docker-Compose

prompt
"docker-compose  怎麼安裝"

盡量要描述具體,所以我連ssh登入hint給的os資訊都一併給 ,這樣好讓他判斷要給我什麼安裝指令

其實先 前我因為相關經驗,有透過個別指令 安裝過Docker,DB,wordpress各自的container,而這次我希望一鍵安裝,所以使用Docker-Componse,同時我希望還是會有一個實體路徑,以確保備份還有未來轉移遷站的議題。

Prompt
"我要建立一個docker file 然後
docker file 希望可以建立一個wordpress跟mariadb,然後他們的實體路徑希望綁到/mnt/db跟/mnt/wordpress"
version: '3.9'
services:
  wordpress:
    build: .
    container_name: wordpress
    ports:
      - "8080:80"  # 將主機的 8080 端口映射到容器的 80 端口
    volumes:
      - /mnt/wordpress:/var/www/html  # 綁定實體目錄
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress_password #記得改一下
      WORDPRESS_DB_NAME: wordpress 
    depends_on:
      - db

  db:
    image: mariadb:latest
    container_name: mariadb-container
    volumes:
      - /mnt/db:/var/lib/mysql  # 綁定實體目錄
    environment:
      MYSQL_ROOT_PASSWORD: root_password #記得改一下
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress  
      MYSQL_PASSWORD: wordpress_password #記得改一下

volumes:
  wordpress_data:
    driver: local
  db_data:
    driver: local

其他

這時若你linux的OS時區跟語系有問題的話(optional),你也可以請他給你指令

最後,其實執行 “sudo docker-Compose up -d”,就可以一次把這些服務弄起來了

再用 “sudo docker ps -a”檢查一下是否有跑不起來的服務

註: 若你想在vm內驗證服務是否有up起來,你也可以再多chatgpt指令,他會教你

第7步: 設定GCP防火牆 (理論上不用管vm的firewall,不然很可能會不小心讓你連ssh都連不進去)

預設GCP是給 https-server所以只有開放443 port,只要點擊 vm清單上的外部ip旁的網路介面,就可以去設定他的防火牆,這時只要加入tcp: port 8080 允許的規則

小插曲: 原面安裝完跑docker-componse up以後,我看沒有錯誤訊息,但我看連外部ip 80 port都連不進去後,一直繞在怎麼允許80 port連線,還想不透去猜 是不是mariadb還是wordpress上配置不對,後來才發現是docker上port mapping是8080: 80,代表是vm的8080 port要connect到container的80 port,所以才發現gcp我怎麼開80port都還是連不到的原因XD,果然開了8080 port就通了!

你的網站理論上就可以運作了,這時只要選擇語系按安裝,就可以一鍵安裝好了(歡呼 ~)

安裝完如下:

後續其實要做到一個比較完整的官網,還有包含domain的註冊,SSL的配置就不細說了

以上架站其實搭配Chatgpt已經可以一站式的完成所有的工程(若不是firewall,弄到好可能不用10分鐘),而不用像當年我必須對每一個安裝的主題(例如mariadb、docker)去做查詢別人的文章或相關的指令 ,真的超級smooth。

有的時候都覺得AI革的命,絕對不只是軟體設計師 ,未來會不會SRE等指令結構化工程相關的人員可能都漸漸被AI取代掉了?或許分工上,不再那麼的壁壘分明了吧,因為這些工具能透過AI自動化就能被介面整合,人力需求肯定會比以前少的

至於wordpress的關鍵字SEO設定,這些屬於工程外的細節就另開一篇章吧~

跑Scrum 或敏捷Agile於產品開發專案管理之心路歷程 初期到穩定期

跑Scrum 或敏捷Agile於產品開發專案管理之心路歷程 初期到穩定期

在產品開發的初期階段,Scrum 和敏捷的方法真的有必要嗎?這是個值得探討的問題。敏捷和 Scrum 在軟體業界很普遍,很多團隊透過看板、每日站會、短衝(Sprint)和回顧會議(Retro)來落實敏捷的理念。然而,這些形式化的流程能不能真的解決實際問題?特別是在突發需求頻繁出現的情況下,可能需要更靈活的處理方式。

初期階段的挑戰:探索與定義基礎

在從 0 到 1 的產品開發過程中,關鍵在於快速探索和實驗,收斂出「理想中的產品」——一個真正解決用戶痛點並具有長期價值的產品。這個階段的主要工作是設計規劃和開發執行,但產品還處於不穩定階段時,是否需要導入 Scrum 的周期性制度,其實需要根據實際情況來決定。

我認為,這個階段更需要有經驗的團隊成員來主導方向。他們要能掌握專案的核心,建立必要的開發規範,比如命名規則、測試流程、自動化測試和部署等等。這些規範是為未來的開發文化打基礎的,比形式化的敏捷流程更為重要。

核心目標:快速驗證與凝聚共識

在產品初期,快速驗證概念和想法是最重要的。我們需要團隊內部的高度共識,而不應該盲目執行敏捷框架裡的所有流程。

例如,每日站會是有價值的,因為它能幫助我們快速對齊進度和方向。但像定期的計劃會議或回顧會議,未必是必需的。開會的重點應該放在解決問題和確認目標,而不是為了執行某種固定的流程。

在實際操作中,我們可能花一整週時間設計和驗證基礎架構,而這些會議更像是靈活的討論,目的是確保團隊方向一致。無論是用什麼形式,只要能達到溝通和協作的效果就可以了。

成員特質與團隊目標

初期階段的成功很大程度上取決於團隊的特質和目標是否一致。這個時期的團隊需要具備創新和探索精神,所有人要能夠靈活應變並投入實驗。如果有成員無法融入這樣的節奏,可能會拖慢整個團隊。

產品穩定後:調整工作方式

當產品進入穩定期,比如 1.0 版本上線,開始有用戶使用,並且 CICD 流程已經成熟時,我們需要根據新的需求來調整工作模式。

穩定期的開發節奏會更加規律,我們可能引入更多的敏捷實踐,比如 Sprint 計劃、回顧會議和里程碑規劃等。這個時候,Scrum 的實踐方式會更有價值,因為穩定的節奏能幫助團隊同步開發進度和目標。

以下是我們實踐的一些要素:

1. 看板(Kanban):確保工作可視化,通常會追蹤兩個 Sprint 內的所有任務。我們使用 Excel 或簡單的工具來輔助管理,確保每個人都能鳥瞰全局。

2. 每日站會(Daily Standup):關注當前的障礙和即將進入看板的任務,解決問題更高效。

3. 計劃會議(Planning):對齊目標和分工,並確保每個成員理解需求。

4. 細化會議(Refinement):拆解功能並進行估算,確保大型需求能夠分階段完成。

5. 展示與回顧(Demo and Retro):每個 Sprint 結束時進行成果展示,前端、後端和文件更新都需要同步完成,確保所有人對進展有清楚認識。

6. 知識管理與文件同步:使用 Excel、Figma 或 Markdown 結合 Wiki 平台,記錄關鍵資訊,讓團隊成員能快速上手並保持靈活。

結論:形式為目標服務

敏捷和 Scrum 的核心是幫助團隊靈活應對變化並提升效率。在產品開發初期,重要的是設計適合當下情境的工作方式,而不是執著於敏捷的形式化流程。專注於快速驗證、團隊共識和基礎規範的建立,能讓團隊在不確定性中保持效率。

當產品進入穩定期後,可以逐步引入更多敏捷的元素,但前提是每項實踐都能真正為團隊和產品帶來價值。

c# 冗餘的 await 調用

c# 冗餘的 await 調用

最近在CodeReview一些程式碼的時候,看到這行字

冗餘的 await 調用

• 如果結果已經是 Task,不需要重複 await,這樣可以簡化代碼。

想起來有時候我們確實在一些方法並沒有回傳值的問題而定義了回傳的Task

但這時候使用端仍往往習慣去await這個Task

這時建議的移除await,”似乎”是反習慣性的建議

因此我想說,用一些具體的例子,看一下在async 方法下 不去await一個 Task方法時,會不會有什麼影響

結論: 不會有影響~

簡易的程式碼如下:

Program.cs

Console.WriteLine("1");

var test = new Test();

test.Method1();

Console.WriteLine("2");

Console.ReadLine();


public class Test
{
    async Task Method2()
    {
        Console.WriteLine("3");
    }
    
    async Task<int> Method3()
    {
        Console.WriteLine("6");
        return 7;
    }
    
    public void Method1()
    {
        Method2();
        Console.WriteLine("4");
        var value = Method3();
        Console.WriteLine("5");
        Console.WriteLine(value.Result);
    }
}


Console的執行順序結果會打印 出什麼結果?

[ChatGPT閒聊]自動化測試要關注什麼?

[ChatGPT閒聊]自動化測試要關注什麼?

我問 ChatGPT,它回答我:

在自動化測試中需要關注以下幾個重點:

1. 測試範圍選擇(Test Scope Selection)

選擇適合自動化的測試用例,通常會選擇那些重複性高、穩定、且在多個版本中都會執行的用例,如回歸測試、API測試等。對於容易變動且難以自動化的部分,則可能更適合手動測試

2. 測試腳本的可維護性(Maintainability of Test Scripts)

要確保自動化測試腳本易於維護,編寫腳本時應保持簡潔、模組化並遵循乾淨的代碼原則。當應用程式更新時,測試腳本需要能夠方便地做出調整。

3. 測試用例的穩定性(Stability of Test Cases)

自動化測試應該要具備穩定性,避免 flaky tests(不穩定測試),即某些測試結果偶爾會通過,偶爾會失敗的情況。可以通過確保測試環境穩定、避免依賴外部因素等方式來提高測試的穩定性。

4. 測試數據管理(Test Data Management)

自動化測試需要有效管理測試數據,確保測試數據是獨立且可控的。這意味著測試腳本不應該依賴於生產環境數據,並且應該能夠生成或重置測試數據。

5. 報告和記錄(Reporting and Logging)

測試報告應該能夠詳細展示測試結果,包括測試通過與失敗的用例、失敗的原因、錯誤日誌等,以方便快速定位問題並作出相應調整。

6. 異常處理和重試機制(Exception Handling and Retry Mechanism)

自動化測試腳本應該具有良好的異常處理機制,能夠在遇到錯誤時記錄詳細信息,並且在合適的情況下進行重試。這可以避免因網絡不穩定或暫時性問題導致的測試失敗。

7. 持續集成(Continuous Integration, CI)

自動化測試應與持續集成工具(如 Jenkins、GitLab CI 等)相結合,確保測試能夠在代碼變更時及時執行,並且能自動生成測試報告,以方便追蹤代碼品質。

8. 測試環境的隔離和一致性(Environment Isolation and Consistency)

測試環境應該與開發和生產環境相隔離,並且要保持一致性,以避免測試結果受到環境差異的影響。可以使用 Docker 或虛擬機等工具來確保環境的一致性。

9. 測試效率和執行速度(Test Efficiency and Speed)

自動化測試需要在合理的時間內完成,過長的執行時間可能會影響開發和發布節奏。因此需要平衡測試覆蓋率和測試效率,並嘗試使用並行執行或分佈式測試來加速測試流程。

10. 選擇合適的測試工具和框架(Choosing the Right Tools and Frameworks)

根據項目的需求、技術棧和團隊熟悉程度來選擇合適的自動化測試工具和框架,如 Selenium、Appium、Cypress、Robot Framework 等。不同工具有其優勢和適用場景,選擇適合的工具能提高開發效率。

關注這些重點,可以有效地構建和維護自動化測試流程,提高測試效率和產品質量。

以上是ChatGPT整理的摘要,若能在產品開發階段就關注可測試性,其實 對於後續導入測試框架會有事半功倍的效果

以下是我的觀點,幾個應該前期產品設計與開發就該涉獵的重點:

首先,最基本的是測試獨立性。雖然環境設定或第三方服務可能有些麻煩,但這些通常可以透過 Mocking 或在指定環境下關閉來解決,視測試重點而定。然而,更麻煩的是如何確保測試數據的獨立性,這直接關係到測試案例的穩定性。如果每次生成測試數據時,數據數量不一致(例如,原本只有 1 筆,重跑後變成多筆,甚至超過 10 次導致新分頁出現),那麼原本會通過的案例可能在第 10 次執行時突然失敗。

設計及維護測試數據本身是一項高成本的活動,因此在開發初期理清數據流相當重要。數據往往存在相依性,例如,有人員資訊但沒有權限資訊,某些功能就無法運作。因此,確定最小可動範圍是功能性測試的第一步,通常專案中有整合可重覆與重置的資料庫整合測試框架,我認為極有助於這一塊議題的發展(以我們是使用了.Net Core + EntityFramework + Respawn套件 (https://www.nuget.org/packages/respawn)達成數據可測試性框架,有興趣可以自行再深入了解。接著,測試的獨立性要求在同一版本或不同版本間能重複執行持續執行。例如,若隨版本更新而需要同步更新測試數據,否則新功能可能會因不相容的數據而壞掉。因此,功能測試時也要考慮數據的相容性。雖然設計上沒有絕對的好壞,但一致性與簡單易懂是設計的原則。

接著,關於程式數據生成,在日常功能測試時,RD 可以設計資料生成機制,將測試任務和迴歸測試任務拓展至 RD 層級,要求他們在各環境中確保重複驗證功能性。這樣 RD 就會設計出透過程式生成數據的機制(大家都怕麻煩),對測試資料的建立與生成很有幫助。例如,我們近期產品開發時,我會規劃一個「示範客戶/公司」,由示範客戶下 來建立獨立的使用者,自有的權限,自有的相關數據,而不會影響其他客戶,並透過程式生成示範產品的基本可動數據,確保每次上版時能自動更新,這樣就能銜接更進階的測試,特別是涉及外部同步的資料。最好隨時備妥可重複的模擬資料,確保測試流程順暢。

註:以上示範了在 Multi-Tenant 架構下,如何在前期設計中獨立控制資料欄位。如果不是採用這種架構,也可以從資料庫層面切割客戶,這樣做其實沒有問題,主要還是跟架構成本有關。但一旦確立了標準的 schema,接下來就是如何確保系統在最小可行範圍內運作。

總結來說,一切還是圍繞在成本效益比(C/P 值)上。多數專案或產品因為看不到短期效益而猶豫不前,這種情況很常見。就像決定一家餐廳的存亡時,還在考慮菜單字體好不好看,這樣很難說服自己行動。因此,效益和成本必須一起考慮。建立自動化測試也是如此,如果產品在撰寫和執行測試案例時不夠靈活,造成困難,這就會成為一大門檻。大多數阻礙你前進的問題,不是工具怎麼用,而是當你開始動手時,不知道能不能處理後續的麻煩。

Playwright在Azure Pipeline配置

Playwright在Azure Pipeline配置

2024年10月2日,颱風天雖然無風無雨,但還是放了颱風假,只好待在家裡研究最近上保哥的 Playwright 課程後的一些心得和小成果。

從在本地環境使用 Node.js 和 TypeScript 建立測試案例到執行,已經有很多文件和參考資料,所以這部分就先略過。

不過對於自動化框架已經有這麼多套了後,為什麼目前選擇playwright呢?上完課初步瞭解後,我自己整理有以下幾點

  1. playwright簡化了selector,重新設計了定位器,符合無障礙網頁設計規範,與autowait,並可以透過 playwright生態與Api支援下 ,測試可讀性也大幅增加,配合強大工具進行錄製,大幅降低了e2e測試案例寫code的困難度,也降低了維護上的成本。
  2. 強大的Mocking的機制,還有諸多黑科技機制 ,不旦可以mocking api , browser 的storage, 有機會達成更多深入的驗證情境(例如pass 人類、簡訊等雙因子驗證)
  3. 支持所有顯示主流語言,包含java , c# , nodejsd的type script,且可以透過 框架一鍵切換,文件也很完整。
  4. 微軟爸爸金主萬歲

其實,E2E的測試,還有一個就是文化面的觀點,就是所有的測試都必須跟RD雙向奔赴,一面測試能夠做到反饋,產品主動優化,這樣正向循環才有助於產品品質的發展。

作為一個自動化測試框架,通常後續整合到專案或產品上有兩個實作方向:

1. 作為前端專案的介面整合測試

在這種情況下,測試案例的版控權限需要開放到前端專案中。因此,如果開發人員以外的人(如 QA、PM)要協助編修測試案例,可能會頻繁變動專案。而且可能還需要在建置過程中處理 localhost 的啟動及套件測試驗證。這樣的測試其實有點像界面的單元測試,只是更依賴於前端專案,並且還要考慮相關的服務或其他環境的設定。

當然這種方式的好處在於,測試流程可以隨著介面的變更一起調整,與前端工程師的工作息息相關。因此,測試案例的防守範圍包含了 RD,可以隨著功能特性(feature)的調整,達到與版本部署的同步。

但維護成本也相對高(跟專案綁在一起的關係)

2. 作為產品介面的回歸測試

這種方式著重於將較為穩定且不常變動的介面優先建置測試案例。目前,我們專案正朝著這樣的 E2E 測試流程發展,希望能針對不同環境(如 tst、uat、preprod、prod)進行測試。藉由Azure Pileine配置獨立的 E2E 測試專案及執行指令,可以靈活地測試不同環境的標的。

獨立了 test 專案後,測試team(當然也可以RD、PM都一起來寫XD)可以依據不同環境規劃測試案例的目錄。透過 npx playwright test 指令,可以方便地測試指定的案例目錄及不同瀏覽器參數。這種方式相當便捷(先前也玩過 Cypress),並且獨立版控的測試專案也帶來了一些好處,例如可以在 CI 流程中靈活分配,無論是每次上版到不同環境時進行或是定期測試、在指定條件下執行測試,都能自由安排。

這次我希望達到的是上述第二種切入點的做法,因為我們的 E2E 測試希望從使用者操作的角度進行,以 “指定環境” 的 Web 站台為測試對象。

因此我們將BASE_URL(測試站台的進入點)作為 參數由外部傳入

另外,因為考慮了playwright的覆用性,因此也把playwright的大部分Task包成另一個 Azure Template

這樣一來,不同環境就可以建置不同的Pipeline去定義 參數後再傳入PipelineTemplate執行標準的測試 Task

Main Pipeline

(以下範例移除了公司的標記,可以依有抽出參數的部分自由參考運用)


pool:
  vmImage: ubuntu-latest

trigger:
  branches:
    include:
      - 'main'

resources:
  repositories:
  - repository: PipelineTemplates
    type: git
    name: {版控}/Pipeline-Templates
  - repository: E2ETesting
    type: git
    name: {專案}-End2EndTesting

name: $(Build.BuildId)

stages:

  - stage: qa
    displayName: 'Run Automation Test - QA'
    dependsOn: []
    jobs:
      - template: template/playwright-template.yml@PipelineTemplates
        parameters:
          BASE_URL: 'https://tst-{站台}/zh-TW/home' #測試站點
          End2EndProjectKey: E2ETesting  #測試專案版控Key
          End2EndProjectName: {專案}-End2EndTesting #測試專案目錄名稱(用於組合路徑用)

PipelineTemplates

(作為Function可以被不同的pipeline參考執行,注意,這個是 playwright-template.yml的內容,另外一個檔案)


parameters:
  - name: BASE_URL
    type: string
  - name: End2EndProjectKey
    type: string
  - name: End2EndProjectName
    type: string

jobs:

  - job: test
    displayName: Run Playwright Tests
    steps:
      - download: none
      - checkout: self
        persistCredentials: true    
      - checkout: ${{ parameters.End2EndProjectKey }}
        persistCredentials: true

      - script: |
          ls $(System.DefaultWorkingDirectory)
        displayName: "Print Working Dir"

      - task: NodeTool@0
        displayName: 'Use Node version 18'
        inputs:
          versionSpec: 18.x

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
        displayName: "Cd E2E Folder and ls folder "

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
          npm ci
        displayName: "NPM Install"

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          ls .
          pwd
          echo $(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/browsers
          cd /home/vsts/
          PLAYWRIGHT_BROWSERS_PATH=/home/vsts/
          npx playwright install --with-deps
           #note 不要用sudo安裝,會裝到/root去專案跑的時候會找不到browser driver
        displayName: "Playwright Install"

      - script: |
          echo "BASE_URL= ${{ parameters.BASE_URL }}"
        displayName: "Print Variables"

      - script: |
          cd ${{ parameters.End2EndProjectName }}
          BASE_URL=${{ parameters.BASE_URL }} CI=true PLAYWRIGHT_JUNIT_OUTPUT_NAME=test-results/results.xml npx playwright test
        displayName: "Run Playwright Tests"
        continueOnError: true

      - task: ArchiveFiles@2
        displayName: 'Add playwright-report to Archive'
        inputs:
          rootFolderOrFile: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/playwright-report/'
          archiveFile: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'

      - task: ArchiveFiles@2
        displayName: 'Add test-results to Archive'
        inputs:
          rootFolderOrFile: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/test-results/'
          archiveFile: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'
          replaceExistingArchive: false

      - task: PublishPipelineArtifact@1
        displayName: 'Publish Pipeline Artifacts'
        inputs:
          targetPath: '$(Agent.TempDirectory)/$(Build.BuildId)_$(System.JobAttempt)$(System.StageAttempt).zip'
          artifact: pipeline-artifacts

      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'JUnit'
          testResultsFiles: '*.xml'
          searchFolder: '$(Pipeline.Workspace)/s/${{ parameters.End2EndProjectName }}/test-results'
          testRunTitle: 'Playwright ADO Demo - $(System.StageName)'
        displayName: 'Publish Test Results'

成果