:::

CentOS安裝fail2ban記事

布丁布丁吃布丁

CentOS安裝fail2ban記事

image 

fail2ban是Linux上的一個以Python寫成的套件,主要功用是當有人登入時使用的帳號密碼錯誤達到指定次數之後,就將該IP擋住一定時間。這個方法可以用來阻止嘗試以暴力法猜測帳號密碼的攻擊,而且可以避免伺服器被DDoS(分散式阻斷服務攻擊)

然而我在安裝fail2ban的過程十分不順利,不像大部分網頁寫得如此輕鬆,我不確定是哪裡出了問題,但是現在我終於讓fail2ban順利運作了。

以下先做一些雜談,然後再來敘述整個安裝過程。


為什麼伺服器的SSH服務需要fail2ban?

現今Linux的管理工具以SSH為主,預設的連接埠是22、允許root可以登入、每次連線都可以嘗試5次錯誤。這個意思是,只要有心人以這個設定不斷地去做SSH連線,總有一天可以猜到你的root密碼,進而取得伺服器的終極管理權。

我之前的伺服器換過連接埠、也禁止root登入(透過修改/etc/ssh/sshd_config的Port設定跟PermitRootLogin no設定),但很遺憾的,它還是被駭客入侵了。不僅修改了我的SSH設定,也把防火牆全部清得一乾二淨,整臺伺服器變成了殭屍伺服器。我對於自己的天真感到非常慚愧,也深深地體會到光是改連接埠跟禁止root登入是不夠的。

fail2ban是搭配Linux的防火牆套件iptables一起運作。當有人猜測密碼達到預設的6次時,就在iptables中設定阻擋他的IP,讓他連連線都連不進來、無法繼續猜測密碼。預設的阻擋時間為10分鐘,更徹底一點可以讓登入失敗的人永遠都進不來。

fail2ban還可以用來阻擋FTP、Apache等其他需要登入的軟體,不過我的伺服器仍是以SSH連線為主,此篇也主要是講如何用fail2ban來強化SSH連線的安全。


安裝環境

Linux發行套件差異非常大,安裝、設定方式也有相當多的不同,敘述安裝的環境是非常重要的一件事情。

  • 發行版本:CentOS Linux release 6.0 (Final)
    這是RedHat系列的發行版本,與Fedora同系。
    (發行版本的資料會記錄在 /etc/redhat-release
  • yum.noarch 3.2.27-14.el6.centos @anaconda-centos-201106051823.i386/6.0
    CentOS的套件安裝主要是以yum為主。在此也是以yum來安裝fail2ban。他會在安裝fail2ban時自動安裝所相依的相關套件,例如python、tcpwrappers等,在此就不列出那些相依套件。
  • fail2ban.noarch 0.8.4-27.e16 @atrpms
    fail2ban的版本。網路上相當多設定都有些分歧,早期似乎是主要將設定寫在fail2ban.conf裡面,但這個版本是寫在 jail.conf 為主。稍後會講到設定的細節。
  • gamin.i686 0.1.10-9.el6 @anaconda-centos-201106051823.i386/6.0
    執行fail2ban的額外必要元件之一。
SSH環境

這個伺服器調整過SSH設定,SSH的連接埠改成了64022。請記住你修改過的SSH連接埠號碼,這在稍後設定fail2ban的時候會用到。


fail2ban的安裝與設定

接下來就要進入fail2ban的安裝與設定手續了。以下步驟都是以root帳戶的身分進行喔。

1. 用yum安裝fail2ban
1-1. 安裝fail2ban

利用yum安裝fail2ban的指令如下:

[root@server ~]# yum -y install fail2ban

yum會列出與fail2ban所有相依套件,並直接確認安裝。如果你想要一一確認哪些套件是否要安裝,請省略 -y 參數。

yum安裝的歷程會記錄在 /var/log/yum 中。如有需要確認已安裝的套件,可以回頭查詢這個記錄檔。

1-2. 設定套件庫atrpms

如果上述步驟不能安裝fail2ban、yum顯示找不到該套件的時候,你需要進入這個步驟。

yum會掃描套件庫來決定要如何安裝套件。然而由於fail2ban並不在預設的套件庫中,所以我們必須手動加入含有fail2ban的套件庫atrpms。

請編輯 /etc/yum.repos.d/CentOS-Base.repo

[root@server ~]# vim /etc/yum.repos.d/CentOS-Base.repo

在最後加入以下設定:

[atrpms]
name=Red Hat Enterprise Linux $releasever - $basearch - ATrpms
baseurl=http://dl.atrpms.net/el$releasever-$basearch/atrpms/stable
gpgkey=http://ATrpms.net/RPM-GPG-KEY.atrpms
gpgcheck=1
enabled=1

這樣就可以利用1-1的語法來安裝fail2ban了。

2. 設定fail2ban

fail2ban的設定檔都在 /etc/fail2ban/ 目錄中,主要有兩個檔案:fail2ban.confjail.conf。以下各別說明需要設定的部份。

2-1. 設定fail2ban.conf

fail2ban.conf的位置在 /etc/fail2ban/fail2ban.conf 當中,請以以下指令開啟:

[root@server ~]# vim /etc/fail2ban/fail2ban.conf

這個設定檔需要調整的部份不多,請修改 logtarget 的參數如下:

#預設的參數
#logtarget = SYSLOG
#調整後的參數
logtarget = /var/log/fail2ban.log

這樣子fail2ban在執行時就會將記錄檔記錄到 /var/log/fail2ban.log 中。

在其他發行版本裏,似乎可以用預設的SYSLOG參數讓fail2ban記錄到指定位置,但是在CentOS中則需要手動指定絕對位置才行。

2-2. 設定jail.conf的backend

fail2ban主要的設定檔是jail.conf,位於 /etc/fail2ban/jail.conf 當中,請以以下指令開啟:

[root@server ~]# vim /etc/fail2ban/jail.conf

jail.conf需要設定的地方頗多,這邊我分成兩個部分來講解。首先先找到 [DEFAULT] 開頭的區塊,並找到backend參數進行設定:

#預設的參數
#backend = auto
#調整後的參數
backend = gamin

我原本使用預設的參數backend = auto,但是這樣fail2ban無法正常啟動。根據Fail2ban的說明,Fedora系的發行版本(包括此篇使用CentOS)如果在記錄中發現了「 fail2ban.comm   : WARNING Invalid command: ['add', 'ssh-iptables', 'auto'] 」錯誤訊息,那就得將backend參數從預設的 auto 改成 gamin

gamin是Linux的套件之一。你可以用yum來安裝它,指令如下:

[root@server ~]# yum -y install gamin

2-3. 設定jail.conf的[ssh-iptables]區塊

位於jail.conf中的 [ssh-iptables] 是用iptables來阻擋SSH嘗試錯誤連線的設定。以下我先列出我的設定,然後再來講述要修改的參數:

[ssh-iptables]
#是否啟用
enabled = true
#過濾名稱,使用預設的即可
filter = sshd
#iptables設定
action = iptables[name=SSH, port=64022, protocol=tcp]
#發生阻擋時的寄信設定
sendmail-whois[name=SSH, dest=pulipuli.chen@gmail.com, sender=root@server.nccu.edu.tw]

#需要掃描的記錄檔
logpath = /var/log/secure
#最高嘗試錯誤次數
maxretry = 2
#阻擋的時間,-1表示永久阻擋
bantime = -1

其中,跟預設值不一樣、需要調整的參數如下:

  • action中的port:預設值是 ssh ,也就是22。但是由於我修改的伺服器的SSH設定,目前的連接埠是64022,所以在此要設定為64022
  • sendmail-whois中的dest:預設是寄到 root ,也就是本機端的root帳戶的信箱。但是由於我並不是常常登入到此伺服器檢查root信箱,這個設定比較沒有意義,所以在此我設定為我自己的信箱位置。當fail2ban啟動、關閉、發生阻擋的時候,它都會寄通知信到這個位置。
  • sendmail-whois中的sender:這個設定會顯示在寄信的來源位置,預設是 fail2ban@mail.com 。為了辨識這個通知是來自於哪一台伺服器,在此最好給予它一個明確的名稱,包括這台伺服器的Domain Name跟目前執行fail2ban的帳戶,例如 root@server.nccu.edu.tw
  • logpath:預設值是 /var/log/sshd.log 。每個Linux發行版的SSH服務記錄登入訊息的位置都不太一樣,CentOS是將登入訊息記錄在 /var/log/secure 中,因此參數必須設置成此路徑。
  • maxretry:最高嘗試錯誤次數。一旦登入錯誤超過這個次數,fail2ban就會發生阻擋事件,將該IP擋在門外。預設是使用 [DEFAULT] 區塊中的maxretry = 5,在此我設定是更為嚴謹的 2。實際上,嘗試登入時可以比maxretry還多一次,例如maxretry = 5,那麼登入時可以錯誤6次才會被擋下。
  • bantime:發生阻擋事件之後,阻擋該IP的時間。預設是用 [DEFAULT] 區塊中的 bantime = 600,單位是秒,也就是10分鐘。但是這個阻擋時間實在太短,有人設定為 86400,也就是一整天。在此我設定為更嚴謹的 -1 ,表示永久阻擋。

jail.conf裡面還有許多其他服務的設定,但是預設都是關閉的,只有ssh-iptables預設開啟。如果你想利用fail2ban阻擋ftp、apache之類的嘗試登入攻擊,那麼你可以修改jail.conf的設定來達成這些目的。但是由於每個Linux發行版本都有所差異,因此參數設定都有些許不同,其他的服務就請上網找尋別人的討論吧。

2-4. 讓fail2ban重新啟動時不會重設阻擋IP規則

在預設的設定中,fail2ban每次重新啟動時都會遺忘被阻擋的IP設定。舉例來說,如果我的電腦因為登入失敗被fail2ban擋掉,那麼只要fail2ban重新啟動,那麼我的電腦又可以繼續去嘗試登入伺服器。

如果要讓fail2ban重新啟動時,不會重設阻擋的IP規則,則可以參考Vinnie Vedi的作法,修改 /etc/init.d/fail2ban 的內容。

修改的指令如下:

[root@server ~]# vim /etc/init.d/fail2ban

首先先找到start()的區塊,加入以下紅字的設定:

start() {
echo -n $"Starting fail2ban: "
getpid
if [ -z "$pid" ]; then
rm -rf /var/run/fail2ban/fail2ban.sock # in case of unclean shutdown
$FAIL2BAN -x start > /dev/null
RETVAL=$?
fi
if [ $RETVAL -eq 0 ]; then
touch /var/lock/subsys/fail2ban
echo_success
/sbin/service iptables restart # reloads previously banned ip's
else
echo_failure
fi

echo
return $RETVAL
}

再來找到stop()區塊,加入以下紅字的設定:

stop() {
echo -n $"Stopping fail2ban: "
getpid
RETVAL=$?
if [ -n "$pid" ]; then
/sbin/service iptables save # saves banned ip's
$FAIL2BAN stop > /dev/null
sleep 1
getpid
if [ -z "$pid" ]; then
rm -f /var/lock/subsys/fail2ban
echo_success
else
echo_failure
fi
else
echo_failure
fi
echo
return $RETVAL
}

這樣就大功告成了。

3. 設定fail2ban開機順序

網路上大部分的說法,都是使用 chkconfig 指令來將fail2ban加入開機自動啟動的設定之中。語法如下:

[root@server ~]# chkconfig --add fail2ban

設定好之後可以重開機測試看看,指令如下:

[root@server ~]# reboot

以下額外講述我在找尋fail2ban設定時,別人遇到的兩種無法正常啟動fail2ban情況:

fail2ban開啟順序必須在iptables之後

由於fail2ban是設定iptables以實作阻擋的功能,所以fail2ban必須比iptables更晚啟動。預設安裝的情況的確是如此運作。

有時候,有人會將iptables防火牆的規則寫在 /etc/rc.local,這是開機時最後執行的指令檔。而這些規則會蓋掉開機時fail2ban的設定。hondap遇到了這種情況,並提出了修改開機順序的方法,但是我覺得並不是很妥當。

我認為iptables的規則不應該寫在/etc/rc.local當中,而應該用以下指令來永久儲存iptables的規則檔:

[root@server ~]# /etc/init.d/iptables save

詳請請看鳥哥的iptables教學

Ubuntu無法正常開啟fail2ban的問題

使用Ubuntu系的使用者可能會遇到開機無法正常啟動fail2ban的問題,這是由於 /var/run/fail2ban/ 目錄在重開機的過程中被移除掉而導致fail2ban無法正常啟動的緣故。詳細的處理方法請看TECH BLOG的Fail2ban does not start after reboot這篇。

4. 啟動fail2ban

啟動fail2ban的語法如下:

[root@server ~]# service fail2ban start

如果你的service指令無法順利運作,也可以利用以下指令啟動fail2ban:

[root@server ~]# /etc/init.d/fail2ban start

需要注意的是,每次fail2ban的關閉、啟動,都會洗掉之前被阻擋的IP設定。舉例來說,如果我的電腦因為登入失敗被fail2ban擋掉,那麼只要fail2ban重新啟動,那麼我的電腦又可以繼續去嘗試登入伺服器。要保留fail2ban阻擋的IP規則的話,請參考2-4的方法修改 /etc/init.d/fail2ban

5. 檢查fail2ban是否運作

檢查fail2ban是否正常運作的方法有許多種,在此舉出三種:一種是用status指令檢查fail2ban的運作狀況,一種是檢查log記錄檔,最後一種則是觀察iptables的設定。

5-1. 檢查fail2ban的stauts

檢查fail2ban的狀態指令如下,而指令後面是正常狀態下的回應訊息:

[root@server ~]# service fail2ban status
Fail2ban (pid 1106) is running...
Status
|- Number of jail: 1
`- Jail list: ssh-iptables

其中 Number of jail: 1 這個訊息指出了我們在上面設定的一個阻擋規則:ssh-iptables,表示我們的設定正確,而fail2ban也有正常運作。

相反的,如果發生了 ERROR  Unable to contact server. Is it running? 錯誤訊息,那麼請依照Fail2ban的FAQ指示一一檢查吧。

5-2. 檢查fail2ban.log的記錄

如果你在上面的2-1步驟中設定了 /etc/fail2ban/fail2ban.conf ,並且正常啟動fail2ban,那麼應該可以找到 /var/log/fail2ban.log 這個檔案。

開啟指令如下:

[root@server ~]# vim /var/log/fail2ban.log

如果正常開啟的話,裡面會有記載以下資料(時間日期會有所不同):

2011-07-20 01:25:46,776 fail2ban.server : INFO   Changed logging target to /var/log/fail2ban.log for Fail2ban v0.8.4
2011-07-20 01:25:46,791 fail2ban.jail : INFO Creating new jail 'ssh-iptables'
2011-07-20 01:25:46,820 fail2ban.jail : INFO Jail 'ssh-iptables' uses Gamin
2011-07-20 01:25:46,955 fail2ban.filter : INFO Added logfile = /var/log/secure
2011-07-20 01:25:46,956 fail2ban.filter : INFO Set maxRetry = 2
2011-07-20 01:25:46,961 fail2ban.filter : INFO Set findtime = 600
2011-07-20 01:25:46,962 fail2ban.actions: INFO Set banTime = -1
2011-07-20 01:25:47,117 fail2ban.jail : INFO Jail 'ssh-iptables' started

相反的,如果fail2ban沒有正常啟動,那麼也可以檢查這個記錄檔,找找看有什麼問題。

5-3. 檢查iptables的規則

如果fail2ban有正常啟動的話,它應該會在iptables中加入fail2ban設定的規則。

觀察iptables規則的指令與結果如下:

[root@server ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
fail2ban-SSH tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:64022

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain fail2ban-SSH (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0

標示紅字的部分是fail2ban加入的規則,這條規則是根據上述2-3裏在jail.conf的[ssh-iptables]的action參數來設定的。

規則會將所有來源IP、目的IP的tcp封包,指定目的連接埠64022的封包轉移到fail2ban-ssh規則鏈(Chain)中,未來如果要阻擋某個指定IP,fail2ban會將阻擋的規則加入fail2ban-SSH規則鏈中。


實際操作fail2ban與應用

安裝與設定完fail2ban之後,接下來就是要測試看看它能不能正常運作。同時我也補充一些其他fail2ban教學比較少講的清除阻擋的方法,以及將要阻擋的IP永久加入iptables防火牆的方法。

5. 測試fail2ban

fail2ban的指令操作大部分可以靠 fail2ban-client 來進行。

5-1. 初始狀態

首先我們先看一下fail2ban中阻擋SSH連線的ssh-iptables的狀態:

[root@server ~]# fail2ban-client status ssh-iptables
Status for the jail: ssh-iptables
|- filter
| |- File list: /var/log/secure
| |- Currently failed: 0
| `- Total failed: 0
`- action
|- Currently banned: 0
| `- IP list:
`- Total banned: 0

上面的訊息表示目前沒有任何IP被阻擋。

5-2. 登入錯誤

接著我在某台電腦利用PieTTY進行SSH連線登入,故意輸入錯誤的帳號與密碼。

2011-07-20_105103 登入錯誤(mask2)

由於我們設定maxretry = 2,所以當錯誤輸入第三次時,這台電腦就被擋下來了。而且錯誤訊息是「Network error: Software caused connection abort」,而不是SSH的登入錯誤訊息。

5-3. 阻擋之後的狀態
[root@server ~]# fail2ban-client status ssh-iptables
Status for the jail: ssh-iptables
|- filter
| |- File list: /var/log/secure
| |- Currently failed: 0
| `- Total failed: 2
`- action
|- Currently banned: 1
| `- IP list: 140.***.***.***
`- Total banned: 1

從狀態中可以看到,剛剛連線的IP已經被列入阻擋項目中。

5-4. 通知信

2011-07-20_111122 gmail (mask)

由於我們在2-3中設置了 sendmail-whois 參數,讓fail2ban在發生阻擋時自動寄送通知信,所以測試中被阻擋的電腦資訊就會自動地寄到我的信箱,如上圖。

fail2ban寄來的信件中,主旨都會以 [Fail2Ban] 開頭,因此可以搭配Gmail的篩選器(filter)功能來管理這些郵件。

6. 清除fail2ban的阻擋

如果有管理者手滑輸入錯誤密碼而被fail2ban擋住,那麼可以透過以下方法來刪除被fail2ban阻擋的IP。

假如來源IP「140.119.1.110」是正常的使用者,而他不小心被fail2ban阻擋了。那麼你應該可以在iptables看到fail2ban加入的阻擋規則(藍字是規則鏈名稱紅字是IP):

[root@server ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
fail2ban-SSH tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:64022

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Chain fail2ban-SSH (1 references)
target prot opt source destination
DROP all -- 140.119.1.110 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0

我們可以用iptables的語法來刪除被阻擋的IP:

[root@server ~]# iptables -D fail2ban-SSH -i eth0 –s 140.119.1.110 -j DROP

其中,fail2ban是規則鏈(Chain)的名稱,請看上面iptables規則訊息中的藍字;紅字的140.119.1.110就是你要刪除的IP。(感謝網友提醒,原本誤寫為-d,表示目標,應該改成-s才對)

需要注意的是,就算用iptables的語法刪除了被阻擋的IP,這個IP依舊會在fail2ban的狀態中留下記錄喔。

網卡代號eth0

如果你有多張網卡,而你設定iptables防火牆阻擋的網卡並不是第一張的「eth0」,那麼你可能需要修改一下這張網卡的參數。關於iptables防火牆詳細的設定教學,請參考鳥哥的網站

7. 重設fail2ban的狀態

只要重新啟動fail2ban就能重置狀態的記錄。

重新啟動fail2ban的語法是:

[root@server ~]# service fail2ban restart

需要注意的是,如果你沒有做過上述2-4的動作、修改 /etc/init.d/fail2ban 的話,那麼在重新啟動fail2ban的時候,被阻擋的IP規則也會一併被清除,這樣該IP又能夠再次嘗試登入你的伺服器了。

8. 在iptables設定永久阻擋的IP

當有人時常要嘗試登入你的伺服器,而多次被fail2ban阻擋在外時,你可以將該IP設定到iptables防火牆的規則中,讓他永久被阻擋在外。

詳細的設定可以參考鳥哥的iptables教學,在此我僅介紹在iptables中加入與刪除阻擋指定IP的語法。舉例來說,「213.131.252.251」這個IP老是要嘗試登入我的伺服器,而我主要的連線網卡代號是「eth0」,那麼我可以設定以下規則,把它永久阻擋在外:

[root@server ~]# iptables -A INPUT -i eth0 -s 213.131.252.251 -j DROP
[root@server ~]# iptables -A INPUT -i eth0 -d 213.131.252.251 -j DROP

如果想要刪除誤加的規則的話,請將 -A 改成 -D,其他語法相同:

[root@server ~]# iptables -D INPUT -i eth0 -s 213.131.252.251 -j DROP
[root@server ~]# iptables -D INPUT -i eth0 -d 213.131.252.251 -j DROP

題外話,「213.131.252.251」就是上次攻擊我伺服器的駭客電腦,這是真的建議要擋掉的一個IP啊!


結語

雖然目前不知道fail2ban是否真的能夠抵擋駭客的攻擊,但是我覺得這是非常重要的一個套件,也希望架設Linux伺服器的網管人員最好都幫你的伺服器安裝一下。

(more...)

「布丁布丁吃什麼?」變更記事

布丁布丁吃布丁

「布丁布丁吃什麼?」變更記事

image

以下記錄這段期間「布丁布丁吃什麼?」blog的零星變更。


更名為「布丁布丁吃什麼?」

有鑑於大家都不會念這個blog的原名「布丁布丁吃?(什麼)」,那我乾脆把它改成「布丁布丁吃什麼?」好了。改名字之後有好一段時間了,有人發現到這個變更嗎?

編輯網站小圖示

最近也發現到Blogger提供了編輯網站小圖示的功能,比起以前那種從語法自訂網站圖示,這個編輯網站小圖示的功能似乎更穩定。詳細內容請見這篇的介紹。

加上Google +1按鈕與Facebook的傳送

image

所謂的隨波逐流,差不多就是這種感覺。

原本我在blog中安裝了Facebook的「讚」,而這個按鈕為我之前寫的Zotero介紹帶來相當大的點閱量。後來Facebook改版,加上了「傳送」的功能,可以讓人指定要分享的對象。我參考重灌狂人的介紹,也將他安裝到這個blog中。

後來Google模仿Facebook的讚,也推出了「+1」按鈕,而這個+1更是會影響到搜尋引擎的排行,對於時常使用Google檢索的人來說是相當實用的功能。這也是參考重灌狂人的介紹來加入的。

不過,我感到棘手的是,這兩個功能都必須要有各自的帳號登入才能啟用。沒有登入的人就不能使用+1或讚的功能,有點違背我對於網站自由平等的期望。而且引用這兩個功能,都會對網頁造成極大的負荷,這讓我時常感到難以取捨。

此外,星等評分功能我就取消了,畢竟那也是拖慢網頁速度的元兇之一。

更新AddThis功能

image

AddThis是集合許多社群網站於一身的分享功能,用簡單的方式就能建置Facebook的like、Goolge+1、分享到噗浪、推特等功能的按鈕。

由於舊版的AddThis被我搞得時常無法正常運作,所以我重新設置了AddThis功能,加入了後來才提供的「偏好按鈕」。在AddThis左邊四個按鈕會依照你使用習慣而動態改變,例如你常常分享到Plurk,那麼該按鈕就會將噗浪設置在前面。

image

如果找不到你要分享的功能,則可以按下最右邊橘黃色的「image」(+圖示),以顯示更多的分享位置。

image

如果你還是找不到你要的,則可以再按下最後的「More」,AddThis會列出它所有的全部分享位置。

取消Blogger內建的e-mail分享按鈕

image

既然有AddThis了,那麼原本的分享按鈕我就不再使用。要使用e-mail分享,請善用AddThis。

題外話,我一直無法使用Blogger內建的分享按鈕,可能是我範本改太多的問題吧orz

調整社交分享與回饋功能的顯示頁面

現在評分、反應等各種社交分享、回饋的功能,全部只會在文章全文中才會出現。首頁、搜尋等條列文章的頁面中,均不會有這類型的功能,以加快網頁的開啟速度。

範本網頁類型調整

自能從範本辨別出首頁、文章列表與文章全文等網頁類型的不同之後,「布丁布丁吃什麼?」的瀏覽速度應該比較快了。

之前測試的時候用了Dropbox的空間,可是後來發現Dropbox空間非常不穩定,樣式表時常下載失敗。後來換回Google Site的空間之後,似乎就穩定多了。儘管Google Site空間需要跳轉,在一些網站速度評比上被視為應該改善的弱點,但還是比Dropbox快上許多。

因為網頁類型可以正確辨識,所以CSS跟JavaScript的使用也更有效率了。

創用CC宣告調整

image

現在縮成一排,而且字體縮小,也比較不會影響到版面。應該是比較順眼吧。

導覽列(sidebar)修正

image

以前導覽列的標籤錯位,還傻傻地用JavaScript去調整。現在直接從範本裡面修正程式碼,整個導覽列的位置就不會錯誤了。

另外,導覽列在文章全文中將會直接隱藏,而不會像以前文章全文中導覽列是以動畫隱藏起來的。

新增功能:CSS Sprties、無限卷頁、圖片延緩載入

CSS Sprites是合併零星圖片、加速圖片下載的技術。

無限卷頁(infinite scroll)會讓使用者讀到文章列表最後的時候自動載入下一頁的內容。

圖片延緩載入則是讓圖片的讀取動作稍緩,等使用者真正要看到這張圖片時才進行載入。

這些都是近期學習並應用的功能,詳細內容請看各篇文章的介紹。

同時也加入了Selenium的功能測試,時常確認功能是否有正確運作。

CSS與JavaScript程式碼壓縮

因為一邊在作最佳化,就想起來還可以作程式碼壓縮這件事情。

程式碼壓縮之後,會刪掉多餘的空行、註解,盡可能降低字句。CSS壓縮後只剩下1/3,JavaScript也有壓縮到1/2的程度。

不過最主要的,還是藉由壓縮來檢查程式碼的錯誤。很多時候寫起來,靠斷行來蒙混過關的程式碼錯誤,在壓縮之後就都造成無法執行的嚴重錯誤。這次也經由程式碼壓縮來把這些小問題修正了。

至於CSS selector最佳化什麼的……以後有空再來研究吧。

Windows Live Writer調整

image

我一向使用Windows Live Writer(以下簡稱WLW)來寫blog,儘管最新已經到WLW 2011版了,但是我現在在用的WLW 2008還是挺好用的,所以沒有升級的打算。

不過之前都很懶得調整WLW的一些細節,寫起文章來總是怪怪的。現在做了以下調整,讓寫blog的時候更有效率:

  1. 跟「布丁布丁吃什麼?」的樣式統一:「布丁布丁吃什麼?」陸陸續續有微調樣式,但是WLW並沒有跟著修正。所以其實很多時候我是在並沒有真的所見即得的情況下寫文章。但是還好,我寫的東西大多時候都結構簡單就是。
  2. 改用新的水平線。以前的水平線會寫死奇怪的樣式設定,讓人難以調整。現在用Dynamic Template Plugin for Windows Live Writer來輸出<hr />,只用class name與搭配CSS來設定,這樣管理起來就方便許多。

其他主要的調整還有:

使用Code Snippet來輸出程式碼

image

值得一提的是,目前我使用Plugin Collection for Windows Live Writer這個外掛來輸出程式碼。作為程式寫作愛好者的我,在文章中時常需要輸出CSS、JavaScript、HTML、Java、PHP、Shell等程式碼。

image

WLW程式碼輸出外掛很多,Code Snippet是我目前看過自訂性較高的外掛。他的主要特色是連CSS的內容都可以自訂。這些CSS樣式直接寫在程式碼中,而不需要引用外部樣式表檔案,速度上比較快,也不容易干擾其他文章的內容。

我之前也用過Syntax Highlighter,它可是相當漂亮的輸出工具,但它是以使用者端的JavaScript去剖析輸出,對使用者來說負擔不小。「布丁布丁吃什麼?」已經夠慢了,就不要再拖慢使用者的閱讀效率吧。

使用Table Plugin來製作表格

image

WLW原始的插入表格功能相當陽春,而且表格寬度只能指定px,不能用百分比來設定。因此我改用了Table Plugin,它可以設定的項目就豐富多了,請見上圖。

Dropbox備份WLW

回想起來,如果沒有WLW,我幾乎就不太想寫blog。現在甚至連Blogger既有的編輯器我都不想開,新增撰寫、草稿、修改文章,全部都仰賴WLW。因為WLW是如此重要,所以現在我還用了Dropbox來備份WLW Portable的資料。也多虧如此,前幾天當我因為多開WLW導致資料毀損之後,又可以從Dropbox輕易地復原了。

其他版面細節調整

  1. 字體大小調整到12pt:這是我自己覺得最適合閱讀的文字大小,預設的10pt讀起來有點辛苦。
  2. 有序項目符號(<ol>,ordered list)省略掉圖示。
    • 可以跟這個無序項目符號(<ul>,unordered)相比較
  3. 壓縮範本中的CSS設定,將不需要參數設定的其他CSS語法移到外部檔案。
  4. 自動目錄大綱也針對水平線進行調整。
  5. 最新回應正確地移除掉換行造成的<br />語法,但是<a>或<img>等語法則就無法移除。
  6. 表格內<p>的縮排取消,表格內字型大小為10pt。
  7. 縮減Plurk跟訪客留言板的高度,讓導覽列不要太長。
  8. JavaScript置底載入,加快網站讀取速度。


結語

這次的修整花了蠻多時間。

很多東西明明覺得只是個小問題,應該可以很快就修好,可是卻還是意外地花了非常多的時間。不過在這段期間也學了很多以前就想學的技術,也不算是在蹉跎時光啦。

這次調整之後,應該有段時間不會再大幅更動了。如果操作上有任何問題,歡迎大家多多提出。

(more...)

臺灣百年圖書館史

臺灣百年圖書館史

THL Project

  • 題名:政大圖檔所台灣百年圖書館史暨數位圖書館先導計畫
  • 網址:http://tlh.lias.nccu.edu.tw
  • 負責建置時間:2007

台灣百年圖書館史暨數位圖書館先導計畫網站的建置之後,我就接手學長建到一半的DSpace系統,也至此與DSpace結下了不解之緣。


台灣百年圖書館史暨數位圖書館先導計畫是政大圖檔所的頂尖大學計畫發展成果。該計畫典藏臺灣圖書館界大事記、各類型圖書館介紹、圖書資訊學教育、圖書館人物、建築與各類文件,以DSpace系統開發建置而成。

這是我第一次參加具有數十人規模團隊的數位典藏計畫,我在陳志銘老師帶領之下從學長接手DSpace系統開發,對於初學Java與JSP的我來說修改得仍不是很成熟,但大部分功能修改已經不成問題。

image

當時我使用Zoomify來製作無差段圖片瀏覽器,似乎頗受好評。在這個計畫中開發出的相當多技術,也造就了後來教育部全國通識教育資源平台的成就。

 

 image

台灣百年圖書館史在作為數位圖書館的功能有張敦媛學姐的標註功能跟SRU (Search and Retrieve via URL) 開放查詢檢索的結果。前者可允許讀者在典藏內容加註筆記,如上圖,這也是我碩士論文的起源;後者是嘗試實作以網址為基礎的檢索協定。


陳佳琪學姊以臺灣百年圖書館史作為碩士論文、研究其資訊架構與搜尋引擎之間的不同。而在計畫結束後幾年,所上老師仍努力想要為此網站建置資料,因此有在持續更新。

不過實際上,能管理此網站的學弟妹應該是越來越少了。跟許多學術計畫一樣,臺灣百年圖書館史計畫也會逐漸凋零吧。雖然令人落寞,但也無可奈何。

(more...)

教育部全國通識網

布丁布丁吃布丁

教育部全國通識網

getcdb 截圖

  • 題名:教育部通識教育資源平臺建構與永續發展計畫 臺灣通識網
    (2010以前的舊名:教育部全國通識教育資源平台建構與永續發展計畫,簡稱全國通識網)
  • 網址:課程資料庫 http://get.nccu.edu.tw:8080/getcdb/
  • 負責建置時間:2006~2010

我在政大圖檔所的學生生涯中,有許多時間投注在這個平台上。我從此計畫剛起步的前兩年負責協助建置、開發,而之後則是擔任指導、諮詢的工作。至今大部分的工作都已經由專任助理們擔任,我也只有在他們有問題時提供詢問,並不直接參與開發。

由於我主要參與的期間,該計畫是以「全國通識網」稱之,所以以下我還是以慣用的「全國通識網」來敘述這個網站。


教育部全國通識教育資源平台是一個跨校大型計劃。內容包括典藏通識課程教材、通識教師資訊、通識實務規劃知識。陳志銘老師負責此計畫中技術部份的子計畫4(2010年之後更名為子計畫3),底下聘有多位助理,而身為兼任助理的我則是負責以DSpace為主的技術指導兼部分程式開發,後期撰寫DSpace技術專書,希望將此機構典藏的技術推廣出去。

全國通識網中分成多個子計畫,包括典藏通識課程的課程資料庫、可供通識教師交流的教室資料庫、學校整體通識計畫的通識實務全景。其中課程資料庫是我投注心力最多的地方。

image

我對DSpace的修改並不僅僅只是版面上的大幅度變動。原本只是典藏資料的DSpace,現在能夠即時在線上播放影片、投影片、文件檔案,這背後還搭配了多媒體檔案轉換的功能來實作。

image

除此之外,為了因應複雜的課程結構,我設計了以XML形式來儲存metadata,突破原本系統在metadata重複資料上的限制。

大部分的技術我都直接寫在「布丁布丁吃什麼?」的blog中,請參考DSpace標籤的連結。不過後來有許多功能改良之後就沒有持續更新,而是希望未來的人可以直接參考DSpace-DLLL中的程式碼。


現在此計畫仍在持續經營中,未來仍有各種豐富的價值有待開發,也能從這上面進行相當多的研究計畫,有興趣的人請與政大圖檔所的陳志銘教授聯絡吧!

(more...)

Blogger編輯網站小圖示功能

布丁布丁吃布丁

Blogger編輯網站小圖示功能

image

大概是最近幾個星期的事情,我發現到Blogger不知不覺在設計 –> 網頁元素中新增了「網站小圖示」的功能。而http://pulipuli.blogspot.com/favicon.ico這個網址也就是我可以自訂的網站圖示囉,如下圖:

image 


強制編輯網站小圖示

網站小圖示的功能有時候在網頁元素的頁面中能看得到、有時候似乎又會被拿下來,可能是因為還是在測試中的樣子。

如果你在你的Blogger管理後台看不到網站小圖示的話,那我在這邊分享一個強制編輯網站小圖示的技巧:

從網址進入網站小圖示編輯功能

網站小圖示功能的網址如下:

http://draft.blogger.com/change-favicon.g?blogID=[blogID]

這個[blogID]可以從你管理後台的網址中看到,例如在新增及排列網頁元素的功能中,你的網址會是:

http://draft.blogger.com/rearrange?blogID=[blogID]

如下圖所示:

2011-07-06_165412

不一定會生效?

其實「布丁布丁吃什麼?」原本也沒有看到網站小圖示功能,而照上述的方式強制進入、上傳圖示之後,它也不會馬上生效。倒是過了幾個禮拜之後,忽然在不知不覺間就生效了,而網頁元素中也可以看到網站小圖示的功能。

不用在範本中設定shortcut icon

之前我寫過一篇介紹「改變你的網站圖示」的文章,這是在範本中設定<link rel=”shortcut icon” href=”圖片連結”/>的方法。不過現在因為Blogger已經允許使用者自訂網站小圖示,那麼這個方法就不需要使用囉。


結語

image

我真正發現網站小圖示生效的時候,是注意到Google閱讀器中網站圖示的改變。而後來經DeDe的詢問,才想說要把這個過程記錄下來。

(more...)

以Selenium IDE建立測試案例:無限捲頁

以Selenium IDE建立測試案例:無限捲頁

 image

無限捲頁是我近日在「布丁布丁吃什麼?」新增的功能。當使用者瀏覽到網頁末端時,他不需要點選「較舊的文章」連結換下一頁,無限捲頁功能就會自動載入下一頁的內容。

現在我要撰寫一個測試案例(Test Case),檢查無限捲頁功能是否能夠正確地運作。


測試目的

  1. 將瀏覽範圍移到畫面尾端,觸發無限捲動功能,然後確定文章數量有比之前更多。

測試指令大綱

無限捲頁測試指令編號清單

功能 測試指令編號
B. 無限捲頁功能  

1. 讀取網頁

B1-1 B1-2 B1-3

2. 觸發無限捲頁

B2-1 B2-2 B2-3

無限捲頁測試指令清單

測試指令編號及測試目的 初始狀態 操作 預期輸出資料或結果
B1-1
開啟首頁
使用者在瀏覽器中開啟首頁網址 瀏覽器開啟首頁網址 首頁正確載入,JavaScript皆有正確初始化
B1-2
確認讀取前文章數量
接B1-1 計算css:.post-outer的個數,存入變數postOuterBefore 變數值低於5
B1-3
顯示讀取前文章數量
接B1-2 在記錄中顯示postOuterBefore的值 變數值低於5
B2-1
捲動頁面到最下方,觸發無限捲頁功能
接B1-3 網頁捲軸往下移動到畫面末端 觸發無限捲頁功能
B2-2
確認無限捲頁功能開始讀取
接B2-1 確認無限捲頁讀取訊息顯示 無限捲頁正常顯示讀取訊息
B2-3
確認無限捲頁功能正常讀取完畢
接B2-2 等待無限捲頁讀取訊息隱藏 讀取訊息隱藏,下一頁文章已經載入
B3-1
確認讀取後文章數量
接B2-3 計算css:.post-outer的個數,存入變數postOuterAfter 變數值低於10
B3-2
顯示讀取後文章數量
接B3-1 在記錄中顯示postOuterAfter的值 變數值低於10
B3-3
確認讀取後文章數量大於讀取前文章數量
接B3-2 確認postOuterAfter大於postOuterBefore true


建立測試指令

這次的測試指令需要用到Selenium的元素計算與變數儲存,也不是單純用錄製(Record)功能就能完成。

詳細的指令內容,請參考Selenium的指令說明。在前一篇「以Selenium IDE建立測試案例:圖片延緩載入」中介紹過的指令,這邊就只會簡略敘述而已。

設定等待時間
  • Commend: setTimeout
  • Target: 60000
  • Value:

由於「布丁布丁吃什麼?」的首頁開啟速度非常慢。為了避免open指令在開啟網頁的過程中因為讀取太久而發生錯誤,所以測試案例正式開始之前,先將逾時限制時間設定為60秒。

B1-1 開啟首頁

接著使用open指令開啟「布丁布丁吃什麼?」的首頁。開啟之後的網頁畫面如下:

image 

B1-2 確認讀取前文章數量

待首頁開啟完畢之後,接下來就是要計算進行無限捲動讀取前的文章數量。Blogger版面中,文章會放在class為post-outer的元素中。而在這個指令中,我想要把讀取前的文章數量存放入postOuterBefore變數中。

既要計算元素的數量,也要存入變數,就要使用Selenium的storeXpathCount指令:

  • Command: storeXpathCount
  • Target: //*[@class="post-outer"]
  • Value: postOuterBefore
    storeXpathCount(xpath, variableName)

參數:

  • xpath - XPath表示式。不需要用XPath的count()函式,Selenium會自動產生。

回傳:

指定XPath符合的元素數量。

這似乎是目前為止第一次用到Value欄位的指令。storeXpathCount會需要兩個參數:第一個參數是XPath表示式,寫在Target欄位中;第二個參數是變數名稱,寫在Value欄位中。

值得一提的是,第一個參數只能用XPath表示式,而不能用平常習慣的CSS選擇器。不過XPath倒也沒這麼難學,我比較一下這兩者的差別。

  • CSS選擇器:css=.post-outer
  • XPath表示式:xpath=//*[@class=”post-outer”]

相信聰明的你應該能夠輕易地從CSS選擇器轉換到XPath表示式吧。

storeXpathCount指令不會發生任何錯誤,執行完畢之後就會直接執行下一個指令。

B1-3 顯示讀取前文章數量
  • Command: echo
  • Target: postOuterBefore的數量:${postOuterBefore}篇文章
  • Value:

為了確認storeXpathCount計算時讀取前文章的數量到底正不正確,在此以echo指令來輸出postOuterBefore變數。你可以注意到postOuterBefore變數在Selenium的指令中是以「${變數名稱}」的方式使用。你也可以在JavaScript中讀取Selenium的變數,用法是「storedVars[ “變數名稱]” ] 」,這在下面指令中也會用到。

echo會在Log欄位中顯示訊息,預期的訊息將會是:

[info] echo: postOuterBefore的數量:7篇文章

由於首頁的文章數量可能會變動,所以數值也會隨之改變。在此只要先記錄讀取前的文章數量即可,以方便進行比較。

B2-1 捲動頁面到最下方
  • Command: runScript
  • Target: window.scrollTo(0, $("#footer-wrapper").offset().top);
  • Value:

接下來執行一段JavaScript程式,讓頁面捲動到頁尾的位置。$(“#footer-wrapper”)是Blogger的頁尾區塊,如下圖:

image

B2-2 確認無限捲頁功能開始讀取

如果無限捲頁有正常執行,那麼此時應該會顯示讀取中的訊息,並有一個小型的動畫圖片,如下圖:

17510352447 首頁圖

這個讀取中的訊息會包含在id為infscr-loading的元素中,所以在此要先等待這個元素顯示,使用waitForVisible指令。

  • Command: waitForVisible
  • Target: css=#infscr-loading
  • Value
    waitForVisible(locator)

    參數:

  • locator - 元素定位器

    回傳

    如果該元素呈現可見狀態(visible),則回傳true;否則回傳false。

這邊使用了waitForVisible,而不是用waitForElementPresent,這兩者的差別在於前者特別指定該元素的狀態是「可見的」,而後者只要該元素存在這頁面中,不管是顯示或隱藏,都會回傳true。

在這邊我們要確定讀取訊息的顯示,所以是用waitForVisible,而不是用waitForElementPresent。

B2-3 確認無限捲頁功能正常讀取完畢

當無限捲頁順利讀取完之後,讀取訊息會隱藏起來。在此用waitForNotVisible來確認無限捲頁讀取訊息是否有正確地隱藏。

  • Command: waitForNotVisible
  • Target: css=#infscr-loading
  • Value:

waitForNotVisible剛好跟waitForVisible相反。當無限捲頁順利讀取完成之後,這個指令就會回傳true,然後進入下一個指令。

B3-1 確認讀取後文章數量

經過無限捲頁讀取下一頁的文章之後,在此再用storeXpathCount來記錄讀取後文章的數量,並存到變數postOuterAfter中。

  • Command: storeXpathCount
  • Target: //*[@class="post-outer"]
  • Value: postOuterAfter
B3-2 顯示讀取後文章數量

跟之前顯示讀取前文章數量一樣地,現在也用echo顯示一下postOuterAfter的結果吧。

  • Command: echo
  • Target: postOuterAfter的數量:${postOuterAfter}篇文章
  • Value:
B3-3 確認讀取後文章數量大於讀取前文章數量

如果無限捲動真的讀取了下一頁的文章,那麼變數postOuterAfter應該要大於變數postOuterBefore才對。在Selenium的指令中,可以利用JavaScript語法來撰寫表示式,並用assertExpression指令來檢查該表示式的結果是否符合預期:

  • Command: assertExpression
  • Target: javascript{storedVars['postOuterAfter'] > storedVars['postOuterBefore']}
  • Value: true
assertExpression(expression, pattern)

參數:

  • expression - 一段表示式,它將會回傳一個值。
  • pattern - 預期比對的值

回傳:

expression符合pattern,則回傳true;否則回傳false。

assertExpression特別適合用來檢查JavaScript執行結果是否正確,不過在此我是用JavaScript來檢查Selenium的變數值。如果要用JavaScript表示式,那麼語法將會是「javascript{程式}」。在JavaScript中取得Selenium變數的方法則是使用「storedVars[“變數名稱"]」。

在assertExpression中的expression參數不使用JavaScript表示式的話,expression通常會被視為字串來處理,而pattern也就是字串比對的模式了。

如果無限捲頁有順利運作,postOuterAfter應該要大於postOuterBefore,而測試也會順利通過。

測試案例結束訊息

在測試案例結束時加上一個訊息,表示測試已經結束。

    • Commend: echo
    • Target: 無限捲頁測試完成
    • Value:
幫測試案例加上註解

image

最後在測試案例中寫些註解,讓人更容易理解測試的內容與目的。這樣子無限捲頁測試案例就大功告成了。


測試運作結果

image

在Selenium IDE中按下「Play current test case」按鈕,就能夠執行目前選擇的測試案例,用以檢查剛剛建立的各種測試指令是否有如預期地運作。

檔案下載
運作影片

結語

總算是寫完了。其實我還想要繼續研究一下Selenium Remote Control伺服器的架設,不過似乎沒有這麼多時間來摸索。所以這次就先學著寫測試案例,以後有機會再繼續研究吧。

(more...)

以Selenium IDE建立測試案例:圖片延緩載入

以Selenium IDE建立測試案例:圖片延緩載入

image

圖片延緩載入是我近日在「布丁布丁吃什麼?」新增的功能。在使用者開啟網頁時,此功能會停止載入瀏覽範圍外的圖片,當瀏覽範圍移至該圖片時,該圖片才正式進行載入動作。

現在我要撰寫一個測試案例(Test Case),檢查圖片延緩載入功能是否能夠正確地運作。


測試目的

  1. 確定瀏覽範圍外的某張圖片沒有載入。
  2. 將某張沒有載入的圖片移至瀏覽範圍內,確定圖片有正確載入。

測試步驟大綱

圖片延緩載入測試指令編號清單

功能 測試指令編號
A. 圖片延緩載入功能  

1. 讀取網頁與初始化

A1-1 A1-2

2. 確認瀏覽範圍外圖片沒有載入

A2-1

3. 移動瀏覽範圍到該圖片,確認圖片載入

A3-1 A3-2

圖片延緩載入測試指令清單

測試指令編號及測試目的 初始狀態 操作 預期輸出資料或結果
A1-1
開啟指定頁面
使用者在瀏覽器中開啟指定頁面網址 瀏覽器開啟指定頁面網址 指定頁面正確載入,JavaScript皆有正確初始化
A1-2
等待圖片延緩載入初始化
接A1-1 確認瀏覽範圍外的圖片出現original屬性 瀏覽範圍外的圖片出現original屬性
A2-1
確認瀏覽範圍外的圖片沒有載入
接A1-2 確認瀏覽範圍外的圖片的src屬性不是圖片正確的網址。(實際上只是佔位圖片) 瀏覽範圍外的圖片的src屬性不是正確的圖片網址
A3-1
觸發圖片延緩載入功能
接A2-1 網頁捲軸往下移動到範圍外的圖片位置 觸發圖片延緩載入功能
A3-2
確認圖片正常載入
接A3-1 等待圖片載入正確的圖片網址 圖片正確的網址順利載入


建立測試指令

由於這個功能並不是像Selenium IDE介紹影片中只是單純的網頁點擊,所以在此並不使用Selenium IDE的錄製(Record)功能,而是純手工建立一條一條的測試指令。

詳細的指令內容,請參考Selenium的指令說明。以下我僅以會使用到的部分來進行說明。

設定等待時間
  • Commend: setTimeout
  • Target: 60000
  • Value:

    下文中每條測試指令都會以上述的方式來呈現。Commend、Target、Value分別對應到Selenium IDE中的三個欄位,見下圖:

    image

    通常Target會作為指令第一個參數輸入,而Value會作為指令第二個參數來輸入。Target不一定每次都是元素定位器,要視指令需要的參數而定。

    Selenium IDE會在下方Reference裡說明指令的用途。在此我也會簡單說明初次使用的指令用法。

setTimeout(timeout)

參數:

timeout - 逾時限制時間,單位是毫秒。超過逾時限制時間的話,該指令會發生錯誤。

使用「open」(開啟網頁)與「waitFor」開頭的指令時,都會用到逾時限制時間的設定。當該指令超過逾時限制時間,則該指令會發生錯誤。預設的逾時限制時間是30秒。舉例來說,使用「open」指令開啟網頁時,當網頁開啟超過30秒仍未完成,則「open」指令會發生錯誤。

由於「布丁布丁吃什麼?」開啟網頁的速度挺沒效率的,很容易就超過預設的30秒。在整個測試正式開始之前,我先用setTimeout將逾時限制時間設定為60秒。由於setTimeout參數的單位是毫秒,60秒 x 1000 = 60000毫秒,所以輸入的參數是60000。

然後接下來就是正式開始測試了!

A1-1:開啟指定頁面
open(url)

參數:

  • url - 要開啟的網址,可以是絕對網址或相對網址(相對網址是相對於Base URL的設定)。

使用open指令就能開啟指定頁面,用法簡單易懂。開啟之後的網頁如下:

image

等待網頁開啟完成之後,就會進入下一個指令。

A1-2:等待圖片延緩載入初始化

由於我在Blog中用了一些AJAX技巧,讀完網頁之後,還需要一段時間才會載入其他的功能,所以我需要知道圖片延緩載入初始化完成的時間。

圖片延緩載入的初始化特徵是會在圖片<img>標籤加上original屬性,用於記錄原本圖片的src網址。因此我們可以藉由偵測此屬性出現的時間,得知圖片延緩載入初始化完成。

同樣的,因為這是需要等待的測試指令,在此我使用waitForElementPresent:

    • Commend: waitForElementPresent
    • Target: css=img[title="2011-06-25_233326 設計 網頁元素"][original="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg67YcGF0_A1mj6f6TLJ_1OlxVn2AgfMBuplkdsneHfVDOpzawOzadfC4AgHqk2jvp6WIE08d6dOd5gyJOrSuVHNWNI4Ehy9F7ea5iMQSVRaxqgwFmGi08vCCHlPey2kesiFd6Vw/"]
    • Value:
waitForElementPresent(locator)

參數:

  • locator - 元素定位器

回傳:

如果元素定位器存在,則會傳true;否則回傳false。

再複習一下元素定位器的用法

  • identify=id
  • id=id
  • name=name
  • dom=javascriptExpression
  • xpath=xpathExpression
  • link=textPattern
  • css=cssSelectorSyntax

在這個例子中,我想要找的是瀏覽畫面以外的一張圖片,他帶有title屬性、值為「2011-06-25_233326 設計 網頁元素」。看它是否有original元素出現。正確的圖片如下:

我參考CSS3選擇器的語法,制定元素選擇器的內容:

css=img[title="2011-06-25_233326 設計 網頁元素"][original=https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg67YcGF0_A1mj6f6TLJ_1OlxVn2AgfMBuplkdsneHfVDOpzawOzadfC4AgHqk2jvp6WIE08d6dOd5gyJOrSuVHNWNI4Ehy9F7ea5iMQSVRaxqgwFmGi08vCCHlPey2kesiFd6Vw/]

image

你可以用Selenium IDE的Target旁邊的Find按鈕來確認元素定位器是否正確。Find按鈕會找尋頁面中你定位的元素,並讓捲軸跳到該處。如果找不到或是語法錯誤,則會顯示錯誤訊息。

等待orginal屬性出現之後,測試就會進入下一個指令。

A2-1:確認瀏覽範圍外的圖片沒有載入

知道圖片延緩載入功能初始化成功之後,接下來我們要確認瀏覽範圍外的圖片並沒有src屬性中載入正確的網址。

  • Commend: assertElementNotPresent
  • Target: css=img[title="2011-06-25_233326 設計 網頁元素"][src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg67YcGF0_A1mj6f6TLJ_1OlxVn2AgfMBuplkdsneHfVDOpzawOzadfC4AgHqk2jvp6WIE08d6dOd5gyJOrSuVHNWNI4Ehy9F7ea5iMQSVRaxqgwFmGi08vCCHlPey2kesiFd6Vw/"]
  • Value:
assertElementNotPresent(locator)

參數

  • locator - 元素定位器

回傳

如果元素定位器並不存在,則回傳true;如果存在,則回傳false。

注意到這邊是以否定情況來測試,而這次元素定位器中css選擇器的屬性是src,而不是前一個指令中的original。

預期src的值會被取代為圖片佔位器的圖片,而不是原本圖片的網址。測試通過的話則進入下一個指令,否則終止測試。

A3-1:觸發圖片延緩載入功能

觸發圖片延緩載入的條件是將瀏覽範圍移動到該圖片的位置。Selenium並沒有移動捲軸的指令,但是卻可用runScript指令執行JavaScript程式碼,來達到移動捲軸的效果。

  • Commend: runScript
  • Target: window.scrollTo(0, $("img[title='2011-06-25_233326 設計 網頁元素']").offset().top);
  • Value:
runScript(script)

    參數

    • script - JavaScript程式碼

    由於我的Blog中已經載入了jQuery框架,所以在這邊我直接用jQuery的offset()方法來取得圖片距離頁面頂部的位置。然後再用JavaScript既有的window.scrollTo(x, y)來指定瀏覽範圍的位置。

    透過runScript指令,即使你可能還不熟Selenium所有的指令,但你可能已經可以開始用任何你熟知的JavaScript方法來操作頁面的功能了。

    runScript指令實際上是在頁尾插入一段<script>標籤,以執行指令中的JavaScript程式碼。例如上述指令則會在頁面最後插入以下程式碼:

    <script type="text/javascript">
    window.scrollTo(0, $("img[title='2011-06-25_233326 設計 網頁元素']").offset().top);
    </script>

    但是Selenium並不能掌控JavaScript程式碼執行時發生的錯誤,runScript永遠都是正確執行,並直接進入下一個指令。

    A3-2:確認圖片正常載入

    當瀏覽畫面移到該圖片的位置時,圖片延緩載入功能應該要能順利運作,將該圖片的src屬性換回正確的網址,讓圖片順利載入正確的圖片。

    上一個指令是移動瀏覽畫面,然後還要等待圖片延緩載入替換圖片的src屬性。儘管這是人類難以察覺的短暫間隔,但這仍是要等待一小段時間。因此在此我用的是waitForElementPresent指令來確認圖片是否有正常地載入。

    • Commend: waitForElementPresent
    • Target: css=img[title="2011-06-25_233326 設計 網頁元素"][src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg67YcGF0_A1mj6f6TLJ_1OlxVn2AgfMBuplkdsneHfVDOpzawOzadfC4AgHqk2jvp6WIE08d6dOd5gyJOrSuVHNWNI4Ehy9F7ea5iMQSVRaxqgwFmGi08vCCHlPey2kesiFd6Vw/"]
    • Value:
    waitForElementPresent(locator)

    參數:

    • locator - 元素定位器

    回傳:

    如果元素存在,則回傳true;否則回傳false。

     

    waitForElementPresent會等待30秒鐘(可由setTimeout指令來改變預設的逾時時間),檢查該元素是否存在。如果圖片延緩載入有順利運作,那麼title為「2011-06-25_233326 設計 網頁元素」的圖片,其src屬性應該順利成為「https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg67YcGF0_A1mj6f6TLJ_1OlxVn2AgfMBuplkdsneHfVDOpzawOzadfC4AgHqk2jvp6WIE08d6dOd5gyJOrSuVHNWNI4Ehy9F7ea5iMQSVRaxqgwFmGi08vCCHlPey2kesiFd6Vw/

    當圖片延緩載入順利啟動之後,這個測試指令就會成功。

    測試案例結束訊息

    Selenium IDE在測試結束之後不太會有明顯的通知。跑過幾次之後,我覺得是應該在測試案例最後加上測試結束的訊息。

    • Commend: echo
    • Target: 圖片延緩載入測試完成
    • Value:
    echo(message)

    參數:

    • message - 要顯示的訊息。

    echo的訊息會顯示在Selenium IDE下方的Log欄位,如下圖:

    image

    如果看到Log中有「圖片延緩載入測試完成」的訊息,那麼就表示這整個測試案例已經跑完了,可以來確認一下有沒有哪些指令發生了錯誤。

    幫測試案例加上註解

     image

    最後,為了讓測試案例容易閱讀,我們可以插入註解(Insert New Comment)來分隔各個測試指令。

    image

    註解的內容寫在Commend欄位裡面,如上圖。註解純粹只是在撰寫時給自己看的,並不會在測試時執行任何動作,也不會在Log中出現。

    至此,整個測試案例就大功告成了。


    測試運作結果

    image

    在Selenium IDE中按下「Play current test case」按鈕,就能夠執行目前選擇的測試案例,用以檢查剛剛建立的各種測試指令是否有如預期地運作。

    檔案下載

    Selenium IDE的測試案例(Test Case)跟測試組合(Test Suite)都是以HTML形式來儲存。

    image

    你也可以直接用瀏覽器來開啟,檢視測試案例的概要內容,如上圖所示。

    運作影片

    螢幕錄影是使用Cute Screen Recorder工具,這段影片沒有錄進聲音喔。


    結語

    這是我第一次製作Selenium測試案例。一開始草草上手,在寫這篇介紹的時候,又把測試案例的內容做過相當多修改。前前後後改了好幾次,也花了不少時間來寫,甚至比單純地學習如何操作Selenium IDE還要花上了更多時間。

    如果有人發現我哪裡講錯了、或是前後不合的話,請務必告知指教。

    image

    題外話,在寫這篇的時候,WLW的排版也變得非常奇怪。左邊的邊界時常在變動,讓我挺擔心最後輸出時是否會有問題。總之,總算是寫完啦。

    (more...)