分類
AI

內網部署LLM(ollama+Open WebUI)

最近在研究提供給內網使用的LLM,因為已經在本機玩過Ollama還蠻容易上手,量化模型也做得不錯(沒有GPU也可以跑Gemma1B,但記憶體要夠大)。

可惜Ollama預設只有提供CLI可以操作,沒有其他介面,但還好Ollama也有提供API存取(http://xxx:11434)。也就是說可以自行實作前端介面(我一開始也是這樣想)來串接ollama API,但考慮到時間和人力成本,很快就轉換方向搜尋合適的前端框架可以快速串接Ollama。

果不其然! 很快就找到Open WebUI,有以下幾個優點
1.介面和ChatGPT相當類似,使用者容易上手
2.串接本地端Ollama相當簡單,只要指定後端Ollama的URL和PORT,一行整合!
3.預設提供帳號管理,模型管理
4.其他好用功能!

基本架構大致完成,後端LLM框架為Ollama,前端網頁框架為Open WebUI,兩者透過預設的Web API來串接,為了達到在不同主機上遷移以及日後擴充的需求,兩者皆以Docker Compose來部署。

為了方便部署和PoC,先把前後端都架設在同一台筆電(ThinkPad L14 Gen 4),64G記憶體,無GPU。因為前後端是透過web api串接,日後要更換或擴充都容易進行。

筆電系統環境是Ubuntu 22.04,主機ip為192.168.2.159,已安裝Docker / Docker compose

透過docker安裝後端框架Ollama

1.先在家目錄建立ollama資料夾(~/ollama)方便管理相關檔案

2.在~/ollama資料夾建立docker-compose.yml,如下

services:
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama-data:/root/.ollama

volumes:
ollama-data:


3.啟動ollama,在~/ollama目錄中輸入
docker compose up -d

4.完成啟動之後就能下載想要測試的模型到本地端,ollama models提供模型清單可以使用,這裡以gemma3:1b做為範例來下載模型,輸入
docker compose exec ollama ollama pull gemma3:1b

5.下載完成後輸入以下指令透過CLI來互動
docker compose exec ollama ollama run gemma3:1b

6.最後開啟瀏覽器輸入http://192.168.2.159:11434,看到 Ollama is running 就代表可以透過Web API提供服務了。

透過Docker安裝前端框架Open WebUI

1.先在家目錄建立open-webui資料夾(~/open-webui)方便管理

2.在~/open-webui資料夾建立docker-compose.yml,如下

services:
openwebui:
image: ghcr.io/open-webui/open-webui:main
ports:
- "3000:8080" #對外介面為3000 Port
environment:
- OLLAMA_BASE_URL=http://192.168.2.159:11434 # 指向 Ollama api
- WEBUI_AUTH=Ture # (選用) 單一用戶模式,關閉多帳號驗證
volumes:
- openwebui-data:/app/backend/data # 資料持久化
restart: unless-stopped
volumes:
openwebui-data:


3.啟動Open-webui,在~/open-webui目錄中輸入
docker compose up -d

4.啟動完成後打開瀏覽器輸入http://192.168.2.159:3000,就能看到Open-webui的首頁

可以看到上方有個gemm3:1b模型,這就是從後端Ollama下載的模型,對後端模型的變更都會更新到Open-WebUI 介面。

因為在docker-compose.yml已把帳號驗證關閉,所以現在進入網頁的使用者都是admin,可以先啟用帳號驗證避免不需要的麻煩,修改open-webui的docker-compose.yml(第一位登入的使用者就是admin)

    environment:
# 允許使用者自行註冊(官方變數名稱)
- ENABLE_SIGNUP=True
# 顯示登入表單(可選,預設 True)
- ENABLE_LOGIN_FORM=True
# 新帳號預設為 Pending,需管理員核准
- DEFAULT_USER_ROLE=pending
- OLLAMA_BASE_URL=http://192.168.2.159:11434
# 啟用認證
- WEBUI_AUTH=True
#- WEBUI_SIGNUP_ENABLED=True # (可選) 允許使用者自行註冊


完成後記得停止Open-WebUI(docker compose down),再啟動Open-WebUI(docker compose up -d)
(如果發現admin無法正常登入,就先刪掉volumes再重新啟動看看)

第一位使用者(admin)登入完成之後,後續使用者都必須先申請帳號並核准後才能登入使用。

當然Open-WebUI還有許多功能,比如說RAG,網路搜尋,模型管理等等,後續再更新吧

分類
tool

JetBrains – IdeaVim插件的搜尋模式輸入中文會顯示打叉方塊

Situation
使用IdeaVim時按下 / 鍵,會進入搜尋模式。預設在此模式下輸入中文會顯示打叉方塊,無法正常顯示中文內容。

Action
File -> Settings -> Editor -> Font -> 點擊 Typography Settings -> 在 Fallback font選擇可支援中文字型的字體,如 Microsoft JhengHei UI -> 點擊 Save或其他確認的按鈕

Result
IdeaVim 搜尋模式已可正常顯示中文

分類
architecture tool

JetBrains – Rider 如何自定義Quick List項目並加入對應的熱鍵

說明

Rider提供大量好用的操作熱鍵來提升開發效率,但有些熱鍵預設的群組或位置不同,有時候你必須跳轉多次才能使用你想要的熱鍵。

Quick List提供一個組合常用熱鍵的方式,讓你把常用的熱鍵組織成Quick List。你可以透過熱鍵叫出Quick List,並在Quick List中再選擇你的操作。

A.建立Quick List項目

1.File -> Settings -> Appearance & Behavior -> Quick Lists

預設已經存在一個Deployment項目,這不是我們的目標先忽略它

2.點擊中央區塊的加號,填寫Display name / Description,主要使用來識別你新建立的Quick List項目

3.點擊Description下方的加號,出現Add Actions to Quick List視窗,在視窗把各個項目點開,選擇你想加到Quick List的操作(可以使用Shift多選),完成後點擊ok離開Add Actions to Quick List視窗。

4.點擊Save離開Quick Lists視窗

B.對Quick List設定對應的熱鍵

(這裡跟一般的熱鍵設定方式是一樣的)

1.File -> Settings -> Keymap-> 選擇Quick Lists中你想設定的Quick List項目

2.左鍵雙擊Quick List項目會出現Edit Shortcut視窗,在該視窗中設定你想要的熱鍵

3.設定完熱鍵之後,點擊Apply -> 完成

C.使用Quick List項目

跟設定熱鍵相同方式,你可以為你的Quick List項目設定熱鍵,透過熱鍵可以輕鬆叫出你的Quick List項目,最後再選擇你想要的操作。

分類
architecture

標準答案不是唯一解

在軟體開發的世界裡,我們常常追求「標準答案」,一種最佳、最高效的解決方案,這種解決方案的重要性無庸置疑。然而隨著時間和經驗的累積,除了尋找標準答案之外,對於多樣性和創新的「包容」是否也相同重要?

當我們面對複雜的項目時,如何在追求最佳解決方案的同時也給予團隊成員發揮創意的空間?這是一個值得深思的問題。我們必須認識到,有時候「標準答案」並非唯一答案,而是一個起點,一個讓我們基於實際情況進行調整和創新的基礎。

儘管在軟體領域中有許多成熟的開發模式和最佳實踐,但我們仍然鼓勵團隊成員提出新的想法。這種包容性不僅提高了團隊的創新能力,也促進了成員間的溝通與合作。這種創新和包容創造出不同的技術選擇和解決方案,能夠幫助我們更好地適應變化快速的技術環境。

最終,我們要學會的是在追求標準答案的同時,也要培養一顆開放的心。這種平衡不僅有助於我們的專業成長,也是領導和管理軟體開發團隊的關鍵。當我們願意聆聽不同的聲音,尊重多元的觀點,我們的項目和產品就能夠更加完善和創新。

在這個快速變化的時代,讓我們一起追求那些標準答案,同時也擁抱那些出乎意料的創意和多元化的解決方案。畢竟,在包容中,我們尋找的不僅是答案,更是一種成長和創新的動力。

分類
Cloud SQL Server

Google Cloud SQL 實體建置紀錄

20240103 更新

Google Cloud SQL 月費問題

依照原文建置的Cloud SQL費用其實不太便宜,總費用是30.40美元

費用裡SQL Server 2019的授權金就要22.16美元(大約7成),剩下的才是其他硬體(CPU/RAM)以及傳輸量的費用(而且還沒用多少傳輸量)。

所以就找了另一個 Azure SQL Database,Azure SQL Database 只要滿足某些條件,確實不用收費,但有限制用量。


本文

最近有需求需要在雲端上建置資料庫,延續之前使用Google雲端的經驗,這次也選擇Google Cloud SQL 來建置雲端資料庫。

前置步驟

請確保已經啟用Google Cloud帳號,可以先使用免費帳號(需要輸入信用卡不會收費,但有抵用額及期限,除非手動啟用完整帳號才˙會收費)

建置實體( SQL Server 2019 Standard)

建置的過程相當簡單,幾個步驟完成後就可以在本機上連結到Google Cloud SQL來進行CRUD的操作

1. 移動到Google Cloud Console畫面

2. 可以建立新專案或使用已存在的專案來建立Google Cloud SQL實體,以下是另外建立一個名稱為 Cloud SQL的新專案

3. 點擊左側邊的”SQL”進入 Cloud SQL 畫面

4. 在Cloud SQL畫面點擊”建立執行個體”

5. 選擇需要的資料庫類型,這裡選擇”SQL Server”

6. 必須啟用 Compute Engine API

7. 這裡就是執行個體的設定項目,比較重要的有資料庫版本和密碼,其他部分依照不同需求設定,設定完成後點擊畫面最下方的”建立執行個體”

8. 執行個體建立完成之後會移動到該個體的主畫面如下,在主畫面中還有一個重要設定需要調整否則沒辦法連線,請點擊”連線設定”

9. 在”連線設定”的”網路”設定中請勾選”公開IP”並點擊下方的”新增網路”,這裡的設定為限制那些外部IP可以存取該執行實體

10. 在新增網路輸入允許存取的IP位址,可以使用 CIDR來標示。如果沒有特別的存取限制,可以輸入0.0.0.0/0 代表不限任何IP位址進行連接(當然還是有帳密的卡控),設定IP後再點擊下方的”完成”

11. 最後再點擊”儲存”

完成!!

以上就是透過 Google Cloud 建立 Cloud SQL 實體的基本操作。接下來透過 DataGrip 來連線看看

開啟DataGrip之後新增一個Data Source,這個Data Source就是用來連接剛剛建立的Cloud SQL實體,在Host填入執行實體的公開IP位址,Port輸入1433,帳號輸入sqlserver,密碼則是輸入執行實體的設定內容

連線成功!!

總結

以上是一個簡單的Google Cloud Cloud SQL實體建立過程,老實說現在的雲服務已經簡化的相當簡單,過程就像在本機上建立資料庫實體。

考慮雲服務的重點之一就是收費,如果使用的是免費帳號,費用當然就是全免。如果是一般帳號,則會根據使用量來決定收取的金額。

分類
工作哲學

無效的計畫是扼殺員工生產力的主因

企業中經常出現一些看似宏大但實際無效益的計畫這些計畫耗費大量資源,更嚴重地削弱員工的效率和動力。為何這些計畫仍在繼續,它們如何影響員工效率,我們如何解決這個問題?

低效益計畫常因缺乏清晰的目標和過度的行政程序而導致員工效率降低。這些計畫通常缺乏明確的方向和可衡量的成果造成員工在執行過程中感到迷茫和挫敗。同時過多的行政負擔和不切實際的期望增加了員工的工作壓力,從而降低了工作滿意度和產出。

企業可以採取以下四項解決方案以減少低效益計畫

1. 有效的計畫評估機制

情況:企業常常因缺乏有效的評估機制而繼續投資於低回報計畫。例如,一項市場研究發現,約42%的企業計畫未能達到預期的商業效益。
解決方案:採用平衡計分卡(Balanced Scorecard)系統,定期檢視計畫的四個維度:財務表現、顧客滿意度、內部流程效率、學習與成長。

2. 增加溝通和鼓勵員工參與

情況:缺乏有效溝通,員工對計畫的認知模糊,如一項調查顯示超過60%的員工對公司戰略認識不清。
解決方案:推行走動式管理(Management By Walking Around)策略,提高管理層與員工之間的日常溝通。

3. 簡化不必要的行政流程

情況:過度複雜的流程消耗大量時間,一項研究顯示員工平均每週花費約28%時間處理不必要的行政事務。
解決方案:進行業務流程再造(Business Process Reengineering),簡化並標準化流程,減少不必要的步驟和時間浪費。

4. 避免不切實際的業務目標

情況:不切實際的目標造成員工壓力,研究指出不合理的工作壓力是員工離職的主要原因之一。
解決方案:採用SMART原則(具體、可衡量、可達成、相關、時限性)設定目標,確保目標實際且可以實現。

透過上述策略的實施,企業不僅可以提升計畫的效益和成本效率,還能有效提高員工的工作滿意度和效率。這些策略需要企業管理層的積極參與和承諾,以確保持續的改善和發展

避免無效益且耗費成本的計畫並採取積極的管理策略是提升企業整體效能的關鍵步驟。

效率是做正確的事,效能是正確的做事!

分類
architecture frame

[架構][框架] 微服務架構:一個多維度的基本探討

微服務的基本概念

近年來,「微服務架構」已成為在軟體工程領域中的熱門話題。這個概念代表一種全新的系統設計和開發方法,它不再是將整個系統視為一個單一,巨大的複雜實體,而是分解成多個小而專注的部分。每個部分都關注於一個特定的功能或業務邏輯,並且可以獨立其他部分進行開發、測試和部署。

在多數的技術文獻和討論中,微服務架構被描述為一種將傳統的單體應用拆分成多個獨立運行的小型服務。這些小型服務各自有自己的數據庫、API 和業務邏輯。服務之間通常通過輕量級的通信協議,如HTTP RESTful API,消息隊列來進行交互。這種方法的主要優勢是允許開發團隊專注單一的業務功能,提高開發效率和質量。

在實際操作中,微服務架構不僅僅是關於將一個大型應用拆分成多個小服務。它還涉及到如何管理這些服務之間的交互,包括數據一致性、事務處理和錯誤恢復等。這些挑戰讓微服務的影響從技術層面涵蓋到組織和文化。

微服務的重要性

微服務架構的重要性在於提供一種能夠更快速、更靈活地反應業務變化的開發方法。在今天的競爭激烈的商業環境中,能夠迅速適應市場和客戶需求的企業往往更有競爭優勢。此外微服務還提供了更好的故障隔離,這意味著當一個服務出現問題時不會影響到其他服務,提高整體系統的可用性。

從業務角度看,微服務架構的意義不僅僅在於技術層面。它允許組織更加靈活地創建和維護應用程序,更好地滿足客戶的需求。由於每個微服務都可以獨立部署,這使得持續集成和持續部署成為可能,從而加速了軟體交付的速度。

微服務的使用概念

正確使用微服務架構,首先需要有一個清晰的業務需求和目標。接著確定哪些功能或業務邏輯應該被拆分成獨立的服務。一旦做出這些決策就需要確保每個服務都有清晰的接口定義,並且有適當的測試和監控機制。最後使用容器化技術和自動化工具來簡化部署和維運。

錯誤使用微服務架構可能會導致系統變得過於複雜和難以管理。如果沒有妥善管理服務之間的通信或依賴關係,可能會導致數據不一致或事務失敗。此外過度拆分服務可能會增加網路通信的開銷,從而影響性能。沒有適當監控和日誌記錄來輔助定位和診斷問題,除錯可能會變得非常困難。

微服務架構與多個軟體領域的重要概念也有密切關聯,你無法獨立看待微服務而忽略這些領域,例如DevOps文化和實踐有助於管理微服務的生命週期;容器化技術(如Docker和K8s)提供了簡單而一致的方式來部署和運維服務;持續集成和持續部署(CI/CD)確保軟體可以快速、可靠地交付給用戶。

微服務的原則

最後,根據以下的原則來實現微服務,可以幫助你保持在正確的方向
1.請關注微服務的本質(服務的分解)而不是技術層面
2.請確保服務之間鬆散耦合(可以獨立開發,測試,部署),避免成為分佈式的大型單體架構
3.最終目標不是實現微服務架構,而是提升開發或維護大型複雜系統的速度

分類
architecture skill

[架構][技巧] AI UnitTest:讓ChatGPT幫助你編寫更好的測試代碼

在軟體開發領域,AI工具的演進提供新方法來編寫和測試代碼。利用AI來輔助撰寫單元測試已經成為一種新的測試策略。

透過提示AI來撰寫測試代碼,可以更快速地生成測試用例,找出潛在的缺陷並解決問題,從而提高軟體開發的效率和品質。


流程

首先對ChatGPT描述使用的技術架構和想進行的步驟,接著在步驟中和ChatGPT互動並請它提供合適的單元測試代碼。(詳細內容請參考下方的”完整流程”)


優點

採用AI輔助撰寫單元測試的效果比預計還好,特別是測試範圍和效率

前者你可以明確指定AI提供測試的範圍,也可以要求AI提供容易遺漏的測試用例,擴大合理的涵蓋範圍。

後者比起手動輸入測試代碼,透過AI產生測試代碼的速度也會有顯著的提升。

提升速度這一點相當重要,許多排斥撰寫單元測試的開發者,主要原因為在開發時程中很難再排入撰寫單元測試的時間,更不用說當業務邏輯變動之後,與其對應的單元測試也必須重構修改,因此拒絕學習或撰寫單元測試。

若透過AI產生或重構測試代碼,就能大幅降低花費撰寫單元測試的時間,也能順便開啟通往TDD的大道。


缺點

不洩漏敏感訊息的情況下,將想測試的邏輯完整表達給ChatGPT是一個可能的問題,因為我測試的邏輯很簡單,ChatGPT很清楚溫度轉換公式,提供測試代碼相當容易,但若你想測試的演算法或情境特別複雜,要完整的表達清楚就沒這麼簡單了。


總結

在現階段透過AI來輔助撰寫單元測試是一種可以探索的方式,事實上整個過程就是簡單的TDD開發流程,而ChatGPT就是我的結對編程同伴,而且這個同伴很有耐心,有求必應,沒有偏見(?)。

在測試範圍和效率都是AI的優勢,能夠大幅度縮短撰寫測試代碼的時間,但在不洩漏敏感資訊的情況下,如何表達複雜的測試邏輯會是一個可能的問題。


完整流程

以下是透過提示ChatGPT來產生測試代碼,再讓開發者來撰寫產品代碼的過程。

首先告訴ChatGPT系統簡述和技術架構和需求,包含測試庫的要求(MSTest)。

目前的系統是一個溫度轉換程式。
技術架構:C#,Winform,用於模型的測試庫(MSTest)。
所有的程式碼都應該使用上述的技術架構來撰寫。
需求:程式應該提供華氏和攝氏的溫度轉換並以測試驅動開發來撰寫。
請你只提供單元測試,我再回答是否通過你的單元測試,以這種循環方式來完成整個過程

好的,以下是使用 MSTest 撰寫的兩個單元測試。一個用於驗證華氏到攝氏的溫度轉換,另一個用於驗證攝氏到華氏的溫度轉換。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsius()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 32.0;
            double expectedCelsius = 0.0;

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheit()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 0.0;
            double expectedFahrenheit = 32.0;

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

這裡假設你有一個 TemperatureConverter 類別,並且該類別有兩個方法:FahrenheitToCelsiusCelsiusToFahrenheit。這些方法都接受一個雙精度浮點數(double)並返回一個雙精度浮點數。這些方法應該進行溫度轉換。

請注意,這些測試中的精確度為 0.01,這意味著如果實際結果與預期結果之間的差異小於或等於 0.01,則該測試將被認為是通過的。

請將此單元測試代碼添加到你的測試項目中,並運行這兩個測試。如果你的溫度轉換代碼正確,則這兩個測試都應該通過。

接著就可以建立Winform專案,並在該專案中新增一個TemperatureConversionTest測試類別並直接貼上ChatGPT提供的完整代碼。

當然,執行測試一定會失敗,因為根本還沒有TemperatureConverter類別。

(如果對測試驅動開發有點了解,應該可以發現這也是驅動測試開發的流程,甚至也可以把ChatGPT當作結對編程的對象)

最後再根據ChatGPT提供的單元測試,實作被測代碼。為了避免洩漏敏感訊息,被測代碼不會提供給ChatGPT。

被測代碼如下

    public class TemperatureConverter
    {
        public double FahrenheitToCelsius(double fahrenheit)
        {
            if (fahrenheit < -459.67)
            {
                throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
            }
            return (fahrenheit - 32) * 5 / 9;
        }

        public double CelsiusToFahrenheit(double celsius)
        {
            if (celsius < -273.15)
            {
                throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
            }
            return (celsius * 9 / 5) + 32;
        }
    }

接著回答ChatGPT,已可通過單元測試,請再提供更多的單元測試。而且在這裡明確指定測試範圍為極限值


我已可通過你提供的單元測試,請再提供更多的單元測試,主要測試範圍為極限值
---
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsiusAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = -459.67;
            double expectedCelsius = -273.15;
            
            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = -273.15;
            double expectedFahrenheit = -459.67;
            
            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
        
        [TestMethod]
        public void TestFahrenheitToCelsiusHighTemperature()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 212.0;
            double expectedCelsius = 100.0;
            
            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitHighTemperature()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 100.0;
            double expectedFahrenheit = 212.0;
            
            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

把上述的單元測試新增到TemperatureConversionTest類別,並執行所有的單元測試,被測代碼可以成功通過單元測試!

接下來想對輸入的不合法數值做一些處理,所以要求ChatGPT對溫度低於絕對零度撰寫測試代碼

我已經可以通過你提供的單元測試,但如果輸入的溫度低於絕對零度,我想丟出一個異常,你可以寫出這種測試嗎?

當然可以。在這裡,我將為溫度低於絕對零度的情況寫出兩個測試。我們期望方法在這種情況下拋出一個異常。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestFahrenheitToCelsiusBelowAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = -500.0; // Less than absolute zero

            // Act
            converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert is handled by the ExpectedException attribute
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestCelsiusToFahrenheitBelowAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = -300.0; // Less than absolute zero

            // Act
            converter.CelsiusToFahrenheit(inputCelsius);

            // Assert is handled by the ExpectedException attribute
        }
    }
}

在這兩個測試中,我們使用了 ExpectedException 屬性來指定我們期望方法在給定的情況下拋出的異常類型。在這種情況下,我們期望方法在溫度低於絕對零度時拋出 ArgumentOutOfRangeException。如果你的溫度轉換方法正確地處理了這種情況,那麼這些測試應該會通過。

把ChatGPT新增的測試代碼貼到測試類別之後,執行單元測試應該會失敗。因為在TemperatureConverter沒有根據輸入的不合法數據丟出異常。

回頭修改TemperatureConverter,主要針對華氏低於-459.67和攝氏低於-243.15,丟出異常

public class TemperatureConverter
{
    public double FahrenheitToCelsius(double fahrenheit)
    {
        if (fahrenheit < -459.67)
        {
            throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
        }
        return (fahrenheit - 32) * 5 / 9;
    }

    public double CelsiusToFahrenheit(double celsius)
    {
        if (celsius < -273.15)
        {
            throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
        }
        return (celsius * 9 / 5) + 32;
    }
}

完成後順利通過單元測試!

最後想請ChatGPT提供它覺得缺乏的單元測試。

我已經可以通過單元測試了,最後我想請提供你覺得缺乏的單元測試

很好,你已經涵蓋了一些重要的測試案例。除了我們已經討論過的測試案例,我們還可以添加一些邊界測試和特殊值測試。以下是一些我們可以添加的額外測試:

  1. 輸入為零的情況:零度華氏對應的攝氏溫度為 -17.78°C,零度攝氏對應的華氏溫度為 32°F。
  2. 一些隨機值的測試:選擇一些隨機的華氏和攝氏溫度值,檢查轉換是否正確。

以下是相應的單元測試:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsiusZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 0.0;
            double expectedCelsius = -17.78;

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 0.0;
            double expectedFahrenheit = 32.0;

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }

        [TestMethod]
        public void TestFahrenheitToCelsiusRandom()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 45.0; // A random Fahrenheit value
            double expectedCelsius = 7.22; // Corresponding Celsius value

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitRandom()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 20.0; // A random Celsius value
            double expectedFahrenheit = 68.0; // Corresponding Fahrenheit value

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

這些測試將增加對你的溫度轉換程式碼的信心。如果所有這些測試都通過了,那麼你的溫度轉換程式碼在這些測試覆蓋的範圍內應該是正確的。請注意,單元測試能幫助我們提高對程式碼的信心,但並不能保證程式碼百分之百的正確。例如,有可能存在我們尚未考慮到的邊界情況或特殊情況。因此,雖然這些測試是一個很好的起點,但可能仍需要進一步的測試和審查來確保程式碼的健壯性。

分類
architecture skill

[架構][技巧]軟體估算系列 – 基本估算技術 – 先計數再計算

在軟體估算中有一些原則可以優先考慮,其中一項就是先計數再計算。以下分別說明如何進行先計數再計算


先計數

具體做法為蒐集對目前專案進行估算最有幫助或最相關的資訊

舉個簡單例子,對於網站開發可以蒐集網站包含的網頁數量,若該網站包含10個頁面,10個頁面就是計數的結果。

另一個例子為釐清需求後,發現系統有10個特性(或功能)需要完成,這也可以當作計數的結果。

先計數的重點是盡量挑選對估算專案最有幫助或最相關的資訊,以下列出幾個優先考慮的方向

1.      和專案規模(時間,成本,人力)最相關的目標

2.      挑選在統計學具有代表性的數量,如計數的數量包含或超過20個

3.      了解計數的相關資訊,資訊代表甚麼意思? 適合目前的專案嗎?

4.      容易取得的計數資訊,在相同的條件下以容易計數的資訊為優先


再計算

接著再對計數的結果進行計算,若計數的結果有長久穩定的歷史紀錄或具體數據,效果會越好

繼續以上述的範例來說明,假設計數出網站包含10個網頁並根據已存在的歷史紀錄評估出1個網頁大約2.5天,那麼10個網頁大約就是25天。

再計算的重點為計算過程盡量透過歷史紀錄和具體數據來評估,特別要避免憑空推測或單憑經驗來計算,原因之一就是個人單體經驗未必適用於所有情況

這點對於經驗老練但不謹慎的開發者反而容易犯錯。

以下列出可用來計數的目標和後續計算所需的歷史資料

可計數的量化目標後續計算所需的歷史資料
特性每個特性用於開發和測試的平均時間
使用案例每個使用案例的平均工作時間
使用者故事每個使用者故事的平均工作時間
功能點每個功能點的平均工作量
頁面數每個頁面平均工作時間
資料層每個資料表的平均工作時間
類別每個類別的平均工作時間
可計數量化目標及後續計算歷史資料


總結

先計數再計算為軟體估算的原則之一,先計數的做法是對於估算目前專案最有幫助的資訊進行計數。請注意幾個優先考慮的方向

再計算的做法為對於計數結果進行計算,當計數的結果有長遠的歷史紀錄或數據,效果會最好。特別注意避免憑空或只憑經驗進行計算

分類
architecture skill

[架構][技巧]軟體估算系列 – 軟體估算的基礎 – 估算技術介紹

不同的估算技術適用於不同的估算環境,以下為選擇估算技術的主要因素


估算的目標

估算的目標分為兩種情況,第一種情況是你已經知道有哪些特性並可針對這些特性來估算時間和工作量,稱為”專案規模“。

第二種情況為你已被限制在固定的預算或工作時間內,必須在這個條件範圍內估算可以交付那些特性,稱為“估算特性”


專案規模

專案規模分為小型,中型,大型專案。

小型專案:

包含5個或5個以下的人員,因為個體生產力的可變性會明顯的影響其他因素,所以一些適用大型專案的估算技術不適用於小型專案。

對於小型專案來說比較好的估算技術為根據實際執行該專案的人員所做的估算(也稱為從下而上)。

中型專案:

中型專案一般為5到25人組成,專案週期為3到12個月。中型專案的優點為幾乎適用於所有大型專案和一些小型專案的估算技術。

大型專案:

大型專案為25人以上,專案週期為6到12個月或更長的時間。在大型項目中根據不同階段選擇不同的估算技術是最準確的。

大型項目的前期階段適用於根據統計或演算法延伸的估算技術(也稱為從上而下),較不適用根據個人提出的估算技術(從下而上)。

大型項目的中期階段可以根據已經產生的專案數據結合從下而上或從上而下的方法來評估,可以到較準確的結果。

大型項目的後期階段適用從下而上的估算技術。


開發風格

開發風格指的是專案的進行方式為直線或迭代,但對於估算技術而言直線和迭代差別在於前者在專案前期需求定義的佔比大,後者為軟體建構之後再進行的需求定義佔比大。

無論是直線或迭代都傾向於從上而下或根據統計的估算技巧開始,最後轉移為從下而上的方式結束,但因為迭代的進行方式會參考自身所產生的數值來改進,所以相比之下更能強化估算準確度。


開發階段

開發階段也可分為三種,各為前期,中期,後期。

前期:

直線式開發風格的前期通常為從問題定義開始到需求完成階段。迭代式開發風格的前期指的是專案初始規劃階段。

中期:

直線式開發風格的前期通常為從需求到初期架構完成階段。迭代式開發風格的中期指的是專案前2~4個迭代。中期的重點是已可取得一些關於專案生產率的數值。

後期:

無論是直線式或迭代式,後期都是指軟體建構中期到最終發布的範圍。


可能的準確度

估算技術的準確度會受3個因素影響,首先是技術本身提供的功能,第二為技術是否應用在合適的情境,第三是技術適用的專案階段。

當然,通常會期望使用最準確的估算技術,但準確度和成本為正比,高準確度的估算勢必付出較高的代價。另一個要注意的準確度會受不確定錐形的影響,考量成本的情況下在可變性低的範圍使用低成本的估算技術也是可以接受的。


總結

選擇估算技術的考慮因素有3個,分別為專案規模,開發階段,開發風格。

最好根據規模,階段,風格選擇不同的估算技術。若有成本的顧慮,就必須考慮可能的準確度和所付出的成本是否合適。