KBTG: Automation Tech The Series
[Automation Tech the Series] Ep.2 เสริมพลัง Test Automation ด้วยการทำ Library
มาทำความรู้จักและเรียนรู้วิธีการสร้าง Library เพื่อเสริมการทำ Test Automation
ต่อเนื่องจากอีพีที่แล้วที่ว่าด้วยเรื่องราวของ M&M Framework ไม่ว่าจะเป็นคอนเซ็ปต์หรือการวาง Structure ที่มีความสำคัญต่อการทำ Test Automation รอบนี้เราจะพูดถึงโฟลเดอร์ “libs” หรือ Library ตัวช่วยสำหรับงาน Test Automation ให้เราสามารถพัฒนา Test Script ได้สะดวกและดียิ่งขึ้น
Test Automation Tool ส่วนใหญ่มักจะมาในรูปแบบ Rich Syntax (โดยเฉพาะ Robot Framework) ทำให้สามารถสร้าง Test Script ที่ซับซ้อนด้วยการใช้ Library สำเร็จรูปได้ เช่น SeleniumLibrary ซึ่งในบางครั้งก็มีความซับซ้อนมากเกินไป 😵 จนกลายเป็นแหล่งที่มาของหลายๆ ปัญหา จะดีกว่าไหมถ้าเราสร้าง Library ขึ้นเอง และย้ายพวก Logic ที่เขียนยากๆ ไปไว้ที่นั่นแทน…
ความจำเป็นแบบไหนที่ต้องทำ Library
- จำเป็นต้องทำ Test Automation บนระบบใหม่
- จำเป็นต้องใช้ Complex Logic เข้ามาช่วยในการทำ Test Automation
- จำเป็นต้องทำ Performance ของการทำ Test Automation ให้ดีขึ้น
- จำเป็นเพิ่มความสามารถให้ Test Script สามารถ Maintain ได้ง่ายขึ้น
และเมื่อคิดจะทำ Library ก็ต้องเตรียมสองสิ่งนี้
- ความรู้ด้าน Programming
- ความเข้าใจใน Tool หรือ Framework
น่าจะเกริ่นมาพอแล้ว เข้าไปที่วิธีการเลยดีกว่า…
Test Automation Tool ที่ทาง M&M Framework ซัพพอร์ตในการสร้าง Test Script ตอนนี้จะมี Robot Framework, Postman, และ UFT ซึ่งในอีพีนี้เราจะพูดถึงแค่ Library สำหรับ Robot Framework เพื่อให้เห็นภาพกันก่อน ส่วน Postman และ UFT จะแยกเป็นอีพีของตัวเองต่างหากครับ
การเขียน Library สำหรับ Robot Framework
Robot Framework เป็น Open Source Framework สำหรับใช้ทำ Test Automation ก็ได้ ทำ RPA (Robotic Process Automation) ก็ดี โดยมี Syntax ที่เขียนง่ายอ่านง่าย ซึ่งจริงๆ แล้ว Robot Framework ก็คือ Package ตัวนึงของ Python นั่นหมายความว่าถ้าเราต้องการเขียน Library สำหรับ Robot Framework เราจำเป็นต้องมีความรู้เกี่ยวกับ Python
ทีนี้มาที่ตัวอย่างกัน เราต้องการที่จะดึงข้อความจาก data.txt โดยเราสามารถเลือกบรรทัดที่ต้องการได้ ถ้าบรรทัดที่เลือกเกินกว่าที่ data.txt มี จะแจ้ง Fail กลับมา
This is text on line 1
This is text on line 2
This is text on line 3
This is text on line 4
This is text on line 5
This is text on line 6
This is text on line 7
This is text on line 8
This is text on line 9
...
This is text on line 10000
ซึ่งเราได้ทำการเขียน Keyword “Get text of line” อยู่บน get.resource
*** Keywords ***
Get text of line
[Arguments] ${string} ${line}
${lines} = Split to Lines ${string}
${length} = Get length ${lines}
IF ${length} < ${line}
Fail Can not get text of line "${line}" because it end at line "${length}"!
END
FOR ${line_no} ${line_string} IN ENUMERATE @{lines} start=1
log ${line_no} - ${line_string}
IF ${line_no}==${line}
Return from keyword ${line_string}
END
END
แล้วนำไปเรียกใช้บน test.robot
*** Settings ***
Library OperatingSystem
Resource get.resource*** Test Cases ***
Test get text from file
${file} = Get file ${CURDIR}/data.txt
${string} = Get text of line ${file} 9999
Log ${string} console=true
จะเจอความจำเป็นและปัญหาดังนี้
- จำเป็นต้องทำ Test Automation บนระบบใหม่ >> ต้องการที่จะเข้าไปเล่นกับ data.txt โดยดึงข้อความจากบรรทัดที่ระบุ
- จำเป็นต้องใช้ Complex Logic เข้ามาช่วยในการทำ Test Automation >> มีการ Loop และมี If Condition ภายใน มีความซ้บซ้อนในการเขียนโค้ดระดับหนึ่ง
- จำเป็นต้องทำ Performance ของการทำ Test Automation ให้ดีขึ้น >> ถ้ามีการ Loop เข้ามาจะให้เสียเวลาเพิ่มขึ้นสำหรับตัว Robot Framework เพราะจะมีการ Log ทุกสเต็ปการทำงาน
- จำเป็นเพิ่มความสามารถให้ Test Script สามารถ Maintain ได้ง่ายขึ้น >> จะเห็นว่าโค้ดอ่านได้ยากและมีการใช้ตัวแปรเยอะ
ดังนั้นให้เราสร้างไฟล์ get.py ขึ้น ถ้าอิงจากสเต็ปการทำงานของ Keyword ด้านบน เราจะได้โค้ด Python ออกมาประมาณนี้
from robot.api.deco import keyword@keyword("Get text of line")
def get_text_of_line(string: str, line: int):
lines = string.splitlines()
length = len(lines)
if length < line:
raise AssertionError(f'Can not get text of line "{line}" because it end at line "{length}"!')
for line_no, line_string in enumerate(lines, start=1):
if line_no==line:
return line_string
เราสามารถเรียกใช้บน .robot ได้โดยเพิ่ม Library บนส่วน Setting
*** Settings ***
Library OperatingSystem
Library get.py
...
จะเห็นว่าถ้าเทียบกันโค้ดทางฝั่ง Python จะจำนวนบรรทัดน้อยกว่า (จากตัวอย่างอาจจะเห็นไม่ชัด) แต่ถ้าเป็น Keyword ที่มีความซับซ้อนมากกว่านี้ ทาง Robot Framework จะต้องเขียนบรรทัดเยอะมาก รวมถึงมีการใช้ Keyword ร่วมเยอะมาก ยิ่งเยอะ Performance ยิ่งแย่ตาม และความซับซ้อนของ Robot Framework ก็ทำให้เราอ่านยากด้วยเช่นกัน 🤖
เหนือไปอีกขั้นโดยการทำ Shared Library
สำหรับการทำ Library ให้กับ Tool อย่าง Robot Framework เราขออัพเกรดไปอีกขั้นด้วยการทำให้เป็น Shared Library อยู่ในท่า Submodules, Repository หรือ Source Code ก้อนนึงที่เราสามารถเรียกใช้ ฝัง หรือแปะให้กับโปรเจค Test Automation ของ KBTG ได้จากพื้นที่ส่วนกลางของ Core Automation
นอกจากนี้อีกประโยชน์นึงของ Shared Library คือเราสามารถ Reuse แอปพลิเคชันอื่นได้ ซึ่งที่ KBTG มีแอปพลิเคชันประมาณ 1,000 ตัว ถ้าเรารู้ว่า Library ของเราเป็นกลางและน่าจะเป็นประโยชน์กับแอปพลิเคชันอื่นด้วย เราก็จะเข็น Library นั้นๆ ขึ้นเป็น Shared Library
เพื่อให้มีความสามารถตามที่เกริ่นมา สิ่งแรกที่เราต้องทำคือวาง Structure ให้กับ Shared Library โดยหน้าตาจะเป็นประมาณนี้
และการเรียกใช้จะเป็นประมาณนี้
นอกจาก Structure แล้ว การเขียนโค้ดเราก็จะประกาศเป็น Class โดยใช้คอนเซ็ปต์ OOP เข้ามาช่วย ซึ่งจะสามารถเพิ่มความสามารถของตัว Library ได้ ทั้งในการทำ Config หรือ Import ที่ง่าย และ Multiple Keyword
ลองดู ExtendedDatabaseLibrary
ExtendedDatabaseLibrary เป็นตัวอย่าง Library ที่เราสร้างขึ้นเพื่อซัพพอร์ต Connection ใหม่ๆ ของ SQL Database (ซึ่งจะต้องรองรับ Connection หลากหลายรูปแบบมาก 😱)
โครงสร้างด้านบนจะซัพพอร์ตการใช้งาน Library ทั้งบน Python3 หรือบน Robot Framework รวมถึง Install เป็น Package บนเครื่องหรือ Environment ได้ผ่าน .whl(wheel) หรือ tag.gz(setuptools) ไปเลย
อ่านเพิ่มเติมเกี่ยวกับ Packaging Python Projects ได้ที่นี่
ตัวอย่างการเขียน Keyword
from DatabaseLibrary import DatabaseLibrary
from .__version__ import VERSION__version__ = VERSIONclass ExtendedDatabaseLibrary(DatabaseLibrary): ROBOT_LIBRARY_SCOPE = "GLOBAL"
ROBOT_LIBRARY_VERSION = __version__ @keyword("Connect Database With JDBC")
def connect_jdbc(self, connection_string, auto_commit=False):
db_api_2 = importlib.import_module("jaydebeapi")
connection_string = connection_string.replace('\\','/')
db_connect_string = 'db_api_2.connect(%s)' % connection_string
self.db_api_module_name = "jaydebeapi"
logger.info('Executing : Connect To Database Using Custom Params : %s.connect(%s) ' % ("jaydebeapi", db_connect_string))
self._dbconnection = eval(db_connect_string)
self._dbconnection.jconn.setAutoCommit(auto_commit)
เราต้องการทำ Connection ใหม่ที่ต้องใช้ JDBC Driver ในการ Connect แต่เราก็ไม่อยากที่จะต้องเขียนส่วนอื่น เช่น Query รวม พร้อมกับอยากที่จะ Reuse Keyword เดิมๆ ที่ตลาดเขามี จึงเป็นคอนเซ็ปต์ในการทำ ExtendedDatabaseLibrary ขึ้นมา
จาก DatabaseLibrary เดิม เราต้องดูความสัมพันธ์ การใช้งาน ตัว Library และวิธีการเขียนข้างในก่อน ถึงจะเขียนท่านี้ได้ ในเคสนี้เรารู้ว่าเราสามารถใช้ db_api_2 connect ที่มีความสามารถ Connect ด้วย JDBC ได้
ดูเรื่อง Inheritance เพิ่มเติมได้ที่นี่
หลังจากที่เราได้ฟังก์ชัน Connect ตัวใหม่แล้ว ก็มาสู่…
ลูกเล่นของ Robot API
@keyword("Connect Database With JDBC")
def connect_jdbc(self, connection_string, auto_commit=False):
ยกตัวอย่างเช่น การใส่ Decorator
@keyword("Connect Database With JDBC")
ซึ่งทำให้เราสามารถเรียก Keyword ในชื่อที่เราอยากให้เป็นได้ โดยไม่ต้องตรงกับชื่อ Method ที่ประกาศ
def connect_jdbc
ทาง Robot Framework เองก็มีเอกสารใน User Guide ให้เรียบร้อย ถ้าต้องการเรียนรู้หรืออยากรู้ลูกเล่นเพิ่มเติมที่ทาง Robot Framework จัดให้ อ่านต่อได้ที่นี่
ส่งท้าย
เป็นยังไงกันบ้างสำหรับการทำ Library หวังว่าเนื้อหาในบทความนี้น่าจะเป็นประโยชน์กับคนที่อยากพัฒนาตัว Test Automation ที่ถืออยู่ให้เจ๋งกว่าเดิมนะครับ 😉
ส่วนอีพี 3จะเป็นอะไรนั้น ติดตามต่อได้ใน…
สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech