💬 討論專區

💬 討論專區

laravel 的單元測試功能 Mail::fake() 簡單原理分析

為了提升系統穩定性,最近替某電商客戶的專案寫單元測試 有一部份核心功能會用到第三方 API,這讓測試變很難寫,因為沒有本地的狀態變化可以比較 想到 laravel 有一個關於 mail 的測試功能,有類似情況 簡單來說就是在 phpunit 中這樣啟動之後 ``` Mail::fake(); ``` 就可以這樣來檢查是否有執行某 api ``` Mail::assertSent(OrderShipped::class); ``` 能否用同樣原理,處理我面對的情況呢? 決定來看一下原始碼! --- 首先是 fake 會直接換掉 Mailer 的實體 `framework/src/Illuminate/Support/Facades/Mail.php` ``` public static function fake() { $actualMailManager = static::isFake() ? static::getFacadeRoot()->manager : static::getFacadeRoot(); return tap(new MailFake($actualMailManager), function ($fake) { static::swap($fake); }); } ``` 可見是實作同樣 interface 的測試專用類別! `framework/src/Illuminate/Support/Testing/Fakes/MailFake.php` 然後寄信就改成,把變數存進陣列即可 ``` public function send($view, array $data = [], $callback = null) { if (! $view instanceof Mailable) { return; } $view->mailer($this->currentMailer); if ($view instanceof ShouldQueue) { return $this->queue($view, $data); } $this->currentMailer = null; $this->mailables[] = $view; } ``` 然後 assertSent 那些就檢查陣列內容,比對一下即可 ``` public function assertSent($mailable, $callback = null) { [$mailable, $callback] = $this->prepareMailableAndCallback($mailable, $callback); if (is_numeric($callback)) { return $this->assertSentTimes($mailable, $callback); } $message = "The expected [{$mailable}] mailable was not sent."; if (count($this->queuedMailables) > 0) { $message .= ' Did you mean to use assertQueued() instead?'; } PHPUnit::assertTrue( $this->sent($mailable, $callback)->count() > 0, $message ); } ``` inside of the `sent` function ``` return $this->mailablesOf($mailable)->filter(fn ($mailable) => $callback($mailable)); ``` --- 所以,目前這樣看起來,在需要測試跟「第三方 API」有關的功能時,可以使用同樣原理 使用 laravel 提供的 mock 功能,把會用到的第三方套件 mock 掉,套件的核心 function 就存個變數或陣列之類的 然後多寫個類似 `assertSent` 這樣的方法,應該就可以做到不錯的測試效果~! 簡單來說,就是設法做出「本地的狀態變化可以比較」就是了 我來往這方向寫寫看,有心得再補充分享~

挑戰串接CodeLove的API

成品連結:https://codingdark.github.io/program/2023/09/18/API-Test.html ##第一步 架部落格 最初看到站長的貼文,提供了本站的API 並且鼓勵大家自架部落格,來嘗試串API 看到文章的當下,覺得有點難度(沒用過github page,API也不熟) 不過看到站長提供的關鍵字`setup a personal blog with github pages` 最終還是決定先google看看 查了之後,發現近年的gitbub簡化了很多 都可以自動部署,根本就是一鍵架站 外加原始碼開源,很多東西都能自己改 所以光是部落格就玩了一天XD 因為本站為CodeLove 愛寫扣 我就創了一個CodingDark 闇寫扣 表示致敬本站 & 本人的中二風格 ##第二步 串API 因為不熟的關係,所以決定先隨便串 呼叫API之後,把data印在console.log 大致上看了一下資料格式 使用上其實就類似陣列,非常方便 沒有想像中的難 確認API可以正常呼叫後 陸續加入一些功能 比方說,用Table把文章列表排整齊、加上[Read more]按鈕 先讓json資料,轉換成網頁能呈現出來的[資訊] ##第三步 程式碼優化 到這邊為止,網頁的畫面已經差不多定型了 剩下的就是回頭檢視程式碼 考量到後續維護 盡可能把重複的程式碼,包裝成function 或是把一群散亂的變數,包裝成object 最後,為了方便擴充文章列表 將原本是寫死的HTML Dom 改成動態產生 目前的部落格頁面 點進去會撈出站長、Tony,以及我的文章列表 後續若想要列出其他使用者的文章 只需新增`setTable(userAccount)`即可 ##結尾 感謝站長提供有趣的API 讓大家可以有練習的機會 雖然目前我在工作上,沒接觸過API 但經過這次的練習,以後若是真的有需要用到 可以更快上手

軟體工程師的履歷表經營:菁英路線 vs 社群路線

某種角度來說,我認為當工程師是很幸運的,能有兩種角度去經營個人品牌:菁英路線 vs 社群路線 --- # 菁英路線 菁英路線就是想辦法得到頂尖軟體公司的認可,比如說 Google Facebook Microsoft IBM 或者台灣本土一些知名軟體公司 或者矽谷一些設點在台灣的軟體公司 這種通常要刷 LeetCode、做大量面試準備、可能大學學歷也要很好看 這種履歷表一但有了一間,後面一間一間換,整個履歷表看起來就很漂亮 薪水好 社會地位高 大家都覺得你超猛 但這路線只適合少數人 # 社群路線 社群路線我覺得簡單、好玩很多 就是每當你的實力有所成長,就找機會大方分享 不論是寫網誌、報名 conference 分享、報名小聚分享 或者參與 open source 討論、發 PR、在 stackoverflow 問答 都是很好的貢獻方式!重要的是,這些貢獻最好能量化、有相關網址連結,在投履歷的時候可以附上 也可以在個人檔案頁面附上,這樣才能越累積越多、讓人一目瞭然 如果是在聊天室參與討論、在 FB 社團參與討論,就很可惜,相關貢獻沒辦法變成單獨網址、附上履歷表 這種路線經營一陣子後,在業界的工作機會、接案機會、合作機會,也會多很多 --- 第一條路線是面對少數人,爭取他們的認同,也就是其他精英、以及公司面試官的認同 第二條路線是面對社群,爭取他們的認同,也就是面對社群五花八門的意見、各類不同族群 就我個人來說,我是走第二條路線,這跟個性有關 --- 身為軟體工程師,我認為至少要在其中一種路線,稍微有所經營 職業生涯上,才會有點安全感,不然會常常有被淘汰的擔憂、面臨社會新鮮人競爭的擔憂

主機硬碟被 mysql 的 binlog 吃光的狀況處理

租用了新的雲端主機,有一天發現全部線上網站都掛了 經檢查發現是硬碟被吃光了,但是我應該沒有存那麼多資料&圖片才對 使用指令慢慢檢查根目錄,在查到 var 時 ``` du -sh /var ``` 發現是 var 資料夾佔用了 125 GB,顯然問題就在這裡 接著使用 ``` du -h /var ``` 發現疑似是 mysql 資料夾,佔用大量硬碟空間 繼續使用指令確認 ``` du -sh /var/lib/mysql ``` 以及查看細節 ``` du -h /var/lib/mysql ``` 發現就是這個資料夾沒錯,cd 進去 ls 看一下,看到大量的 binlog 檔案,命名類似這樣 ``` ... binlog.000738 binlog.000739 binlog.000740 ... ``` 經研究,是 mysql 某種備份機制,暫時不深入研究,我目前不想使用這個備份功能 --- 輸入以下 SQL 緊急移除這些備份 ``` PURGE BINARY LOGS BEFORE NOW(); ``` 硬碟緊急狀況排除! --- 接著要避免之後再出現這個問題 參考這篇做法 https://askubuntu.com/questions/1322041/how-to-solve-increasing-size-of-mysql-binlog-files-problem 建立獨立檔案,方便日後維護 ``` touch /etc/mysql/mysql.conf.d/howtomakeaturn.cnf ``` 放入以下內容 ``` # Added by howtomakeaturn at 2023-08-15 because of 100% disk usage issue. [mysqld] log_bin = # turn off binlog_expire_logs_seconds = 86400 # 1 day max_binlog_size = 104857600 # 100M ``` 完成之後重啟資料庫 ``` service mysql restart ``` 大功告成!應該大致關閉 binlog 功能了,持續觀察幾天看看!

Laravel 5.8 升級到 8.83 版本心得&筆記分享

手上有多個網站 都是 Laravel 5.8,放在一台 Ubuntu 18.04 的機器上(租用 Linode 機器) 四、五年了,一直沒去更新,最近終於下定決心來更新 首先把 Laravel 5 -> 6 -> 7 分多次升級,然後部署 都還算順利,官方的 upgrade guide 很清楚,簡單照做就完成 但是無法升到 laravel 8 因為需要 php 8 我那台 ubuntu 18.04 安裝 php 8 一直失敗 直接升級 OS 也失敗,一堆問題會出現 最後決定,在 Linode 租一台新的 Ubuntu 22.04 然後把專案通通搬過去 --- 因為專案中很多「用戶上傳的圖片」放在 local,所以都需要一起搬運 最後是用 scp 硬搬檔案 然後用 mysqldump 搬運資料 才終於順利升級到 laravel 8.83 了 詳細用到的指令大概像這樣 ``` scp -r [email protected]:/{PATH}/{FOLDER} /{PATH} scp -r [email protected]:/etc/apache2/sites-available/{CONF_FILE} /etc/apache2/sites-available/ a2ensite service apache2 reload create mysql user & database with phpmyadmin mysqldump --column-statistics=0 --default-character-set=utf8mb4 -h xxx.xxx.xxx.xxx -u {USER} --single-transaction -p{PASSWORD} {DATABASE} > /home/dump.sql; mysql -u root -p{PASSWORD} {DATABASE} < /home/dump.sql artisan down legacy site composer install -> new site is now online ``` --- 簡單筆記分享一下,希望能幫到一些人~ # Refs - https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-22-04#how-to-find-your-server-s-public-ip-address - https://itslinuxfoss.com/install-laravel-ubuntu-22-04-lts/ - https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04 - phpmyadmin password left blank, so it's random now - https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-phpmyadmin-on-ubuntu-22-04 - skip the whole Step 2 — Adjusting User Authentication and Privileges - `ssh-keygen` because I need a public key

[技術文章] 一篇文章馬上帶你學會JQuery!

## 前情提要 還在問說要不要學JQUERY?別問了,學就對了,沒人說學了一定要用好嗎。 以下讓我們先看一段純純的JS,就跟國中生的戀愛一樣, 牽牽小手,超純。 ## JS純 ``` // 純 JavaScript 選取元素清單 let list = document.querySelectorAll(".navigation li"); function activeLink(event) { // 使用 forEach 迴圈移除所有元素的 "hovered" 類別 list.forEach((item) => { item.classList.remove("hovered"); }); // 在滑鼠移過的元素上新增 "hovered" 類別 event.target.classList.add("hovered"); } // 使用 forEach 迴圈為每個元素加上滑鼠移過事件的監聽器 list.forEach((item) => { item.addEventListener("mouseover", activeLink); }); ``` ## 轉大人 接下來我們要轉大人了。 ### 選擇器 關於querySelectorAll這種querySelector很簡單,JQ都是用$這個字符來選取。 ## 修改class item.classList.remove("hovered"); event.target.classList.add("hovered"); 這兩個的話JQ有寫出兩隻API: removeClass addClass 所以很方便很好用 ## 事件監聽 addEventListener的話JQ則是用on來表達,這個看過幾次就能夠理解了唷! ## JQ的寫法寫出來 ``` // 使用 jQuery 選取元素清單 let list = $(".navigation li"); function activeLink() { // 移除所有元素的 "hovered" 類別 list.removeClass("hovered"); // 在滑鼠移過的元素上新增 "hovered" 類別 $(this).addClass("hovered"); } // 使用 jQuery 的 each 方法為每個元素加上滑鼠移過事件的監聽器 list.each(function () { $(this).on("mouseover", activeLink); }); ``` ## 心得 JQ到底有沒有被拋棄或是需不需要學習,其實都是假議題,一堆人嘴上用JQ,然後嘴砲JQ 我們身為學習者其實就是花個一小時 就能學起來的東西,沒必要故意閉著眼睛不去了解。 以上,一篇文章馬上帶你學會JQuery! 比較想要玩切版可以看這個: https://ithelp.ithome.com.tw/articles/10312922 [【前端動手玩創意】置頂按鈕,老梗經典|帶你的網頁搭電梯](https://ithelp.ithome.com.tw/articles/10312922)

【前端動手玩創意】動態生成的藝術|小心,亂改DOM你可能會被打臉。

## 目錄 ##### [【前端動手玩創意】等待的轉圈圈效果 (1)](https://ithelp.ithome.com.tw/articles/10311621) ##### [【前端動手玩創意】google五星評分的星星(2)](https://ithelp.ithome.com.tw/articles/10311643) ##### [【前端動手玩創意】CSS-3D卡片翻轉效果(3) (今天難度頗高,想挑戰再進來!)](https://ithelp.ithome.com.tw/articles/10311672) ##### [【前端動手玩創意】一句CSS做出好看的hero section!(4)](https://ithelp.ithome.com.tw/articles/10311691) ##### [【前端動手玩創意】創造一個Skill bar(5)](https://ithelp.ithome.com.tw/articles/10311756) ##### [【前端動手玩創意】遮蔽廣告(D卡未登入)腳本、自定義新增名單(6)](https://ithelp.ithome.com.tw/articles/10311999) ##### [【前端動手玩創意】前端canvas截圖的招式!竟然有三招,可存成SVG或PNG (7)](https://ithelp.ithome.com.tw/articles/10312001) ##### [【前端動手玩創意】讓你的PDF檔案更難被抓取(8)](https://ithelp.ithome.com.tw/articles/10312046) ##### [【前端動手玩創意】哇操!你敢信?花式寫todo-list,body裡面一行都沒有也能搞?(9)](https://ithelp.ithome.com.tw/articles/10312056) ##### [【前端動手玩創意】卡片製作,才不是!是卡片製作器!(10)](https://ithelp.ithome.com.tw/articles/10312066) ##### [【前端動手玩創意】太屌了吧!?用Class(類)製作Jquery的效果!(11)](https://ithelp.ithome.com.tw/articles/10312114) ##### [ 【前端動手玩創意】置頂按鈕,老梗經典|帶你的網頁搭電梯(12)](https://ithelp.ithome.com.tw/articles/10312922) ## 前情提要 一陣子沒寫前端動手玩創意了 今天要來點好料的 我們談談關於藝術 對 就算是寫程式我們也是可以有藝術氣息的 別這麼驚訝XD 假設我有一組資料 一般人來說 最直覺的就是把html裡面的元素寫出來寫死 直覺菜鳥就是寫出來啊 ``` <div id="items_list"> <li class="buy_item">1.紅茶 <div class="price">10</div> <div class="del_btn">X</div> </li> <li class="buy_item">2.奶茶 <div class="price">15</div> <div class="del_btn">X </div> </li> <li class="buy_item">3.綠茶 <div class="price">30</div> <div class="del_btn">X</div> </li> </div> ``` 要更新則是就去更改DOM就好了 反正我都學會JS了 有什麼大不了(? 但是現在已經進入大前端時代了就算你寫原生 也會有data model的概念 這部分可以參考站長阿川寫過的好文 裡面的文筆跟邏輯 不誇張 是我見過「華文圈寫得最好的前端教學」 尤其就是這篇 關於data model的介紹 切入點非常的有趣也很有道理 有興趣的自己去花心思讀看看 [認識 data model 與 render function](https://codelove.tw/@howtomakeaturn/post/9xLo5q) 稍微有點料的前端學習者 本身就會寫一寫 想說:既然有一隻API是createElement 那是不是我可以整個html都用js寫出來(? 這是個超級有趣的觀點 ## 關於data render的概念: 「是不是我可以整個html都用js寫出來」 相傳牛頓提出三大定律以後 認為地球和八大行星一開始的慣性力叫「上帝的第一推動力」 實際上這個: 「整個html都用js寫出來」 也是推動前端往框架邁進的思考第一步 可以說是前端慣性的上帝推力了 當然如果你有打籃球 也可以比喻成上籃的第零步XD ## 觀念筆記 ### html程式碼 ``` <div id="buylist"> <h1>myBuylist 購物清單</h1> <div class="buyitem"> <label>產品名</label> <input/> <label>價錢</label> <input/><span class="addbtn">+新增</span> </div> <div id="items_list">這邊準備用JS插入東西所以這段只是說明文字</div> </div> ``` 把整個頁面的元素寫出來 但是資料的區塊保留著 因為等等我們要用JS的API去動態生成 未來直接data render 方便更新與管理 ### JS程式碼 ``` var shoplist = {}; shoplist.name = "購物清單"; shoplist.list = [ { name: "吹風機", price: "300" }, { name: "麥克筆", price: "20" }, { name: "筆記型電腦", price: "29300" }, { name: "Iphone 9", price: "5200" }, { name: "神奇海螺", price: "1000" }, ]; var item_html = "<li id={{id}} class='buy_item'>{{name}}<div class='price'>{{price}}</div><div class='del_btn'>X</div></li>"; var total_html = "<li class='buy_item total'>總價<div class='price'>{{price}}</div><div class='del_btn'>X</div></li>"; var total_price = 0; for (var i=0; i < shoplist.list.length; i++) { var item = shoplist.list[i]; total_price += item.price; var current_item_html = item_html.replace("{{num}}", i + 1) .replace("{{name}}", item.name) .replace("{{id}}", "buyitem_" + i) .replace("{{price}}", item.price); $("#items_list").append(current_item_html); } ``` 這邊值得關注的一點是資料的處理 也就是數據結構上使用物件與陣列去儲存資料 為什麼要學 陣列 為什麼要學物件 這兩個都是抽象的名詞 他們都是為了來處理資料所規劃的兩種不同內容 ## 關於物件與陣列 物件就像是RPG角色 會有很多的屬性 魔法 敏捷 力量 血量 這些屬性都是被附加在person上面 所以person.blood是一個資料 person.power是一個資料 person.magic是一個資料 最後都存在person裡面 這個person就是一個含有很多屬性的物件 感覺就是有血有肉很3D 而陣列就比較平面化 就是很多數字排成一列而已 其實沒什麼特別需要關注的 這兩個也不會不好區分 只要稍微做個比喻 都很好理解 以我們的code來講解 shoplist就是一個物件 含有一些屬性 包含名字與清單 因為清單很多 所以清單用一個陣列儲存 陣列裡面放很多個物件 這些物件就是每一個清單包含的那些購買項目 其實不用太在意誰是誰因為用看的就很清楚 最後我們用迴圈去把shoplist裡面的每一筆資料都丟進去 中途replace是文字處理 把我們設置的{{name}}這種花括號的東西換成真實資料 這部分有點樣板語言的感覺XD 其實非常有趣 很值得玩味 ## 心得 這次比較多實在的JS程式碼 就不只是玩玩切版了 算是有點靠近框架的部分(◉3◉) 動手玩創意這個系列其實靈魂之處就在於「有趣」兩個字 坊間把前端描繪成為了找工作的銅臭味 以及把切版講成無聊的工匠苦力活 我認為都是錯誤的解讀 我認為 只要是有趣的地方就是前端最有價值的地方 而不是誰學了那個框架 誰又認為哪個東西比較厲害 哪個語言比較屌 那都是很無聊的文字遊戲罷了 如果你喜歡這樣子的前端 請跟著這個系列繼續走(→ܫ←) 我們才只是在桃花源外的小溪旁玩水而已呢! 未來會往天空飛翔哦XD