Golang ทำ CRUD Service APIs (No Database) พร้อมเขียน Test ด้วย

ช่วงนี้ผู้เขียนกำลังเริ่มเรียนรู้เกี่ยวกับ Golang มาสักระยะ วันนี้ก็เลยอยากจะพามาลงบทเรียนการเริ่มพัฒนา Service API ด้วยภาษา Go โดยบทความนี้จะขอ Coding โดยใช้ Echo Framework สำเร็จเพื่อรวดเร็วในการพัฒนา Web Service API มาเริ่มกันเลย

เตรียมความพร้อมก่อน

  • ต้องการ Go runtime เวอร์ชั่น v1.13 หรือมากกว่า สำหรับ Echo v.4
  • IDE หรือ Editor (VScode,Sublime,Atom) แล้วแต่ความถนัด (Vscode ติดตั้ง Extensions Golang)
  • Postman IDE หรือ Terminal ตามความถนัด เช่นกัน

เริ่มโค๊ดกันเลย

  • initial go project go mod init poolsawat.com-echo-crud
  • download echo framework libs
[email protected] poolsawat.com-echo-crud % go get github.com/labstack/echo/v4
go: added github.com/labstack/echo/v4 v4.9.0
go: added github.com/labstack/gommon v0.3.1
go: added github.com/mattn/go-colorable v0.1.11
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/valyala/bytebufferpool v1.0.0
go: added github.com/valyala/fasttemplate v1.2.1
go: added golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
go: added golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
go: added golang.org/x/sys v0.0.0-20211103235746-7861aae1554b
go: added golang.org/x/text v0.3.7
  • สร้าง models/user.go
package models

type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}
  • สร้าง routes/user.go
package routes

import (
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
	"poolsawat.com-echo-crud/models"
)

var (
	seq = 1
)

type RouteUserHandler struct {
	Users map[int]*models.User
}

//----------
// Handlers
//----------

func (h *RouteUserHandler) CreateUser(c echo.Context) error {
	u := &models.User{
		ID: seq,
	}
	if err := c.Bind(u); err != nil {
		return err
	}
	h.Users[u.ID] = u
	seq++
	return c.JSON(http.StatusCreated, u)
}

func (h *RouteUserHandler) GetUser(c echo.Context) error {
	id, _ := strconv.Atoi(c.Param("id"))
	return c.JSON(http.StatusOK, h.Users[id])
}

func (h *RouteUserHandler) UpdateUser(c echo.Context) error {
	u := new(models.User)
	if err := c.Bind(u); err != nil {
		return err
	}
	id, _ := strconv.Atoi(c.Param("id"))
	h.Users[id].Name = u.Name
	return c.JSON(http.StatusOK, h.Users[id])
}

func (h *RouteUserHandler) DeleteUser(c echo.Context) error {
	id, _ := strconv.Atoi(c.Param("id"))
	delete(h.Users, id)
	return c.NoContent(http.StatusNoContent)
}

func (h *RouteUserHandler) GetAllUsers(c echo.Context) error {
	return c.JSON(http.StatusOK, h.Users)
}
  • สร้าง server.go file กำหนดเป็น main package
package main

import (
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"poolsawat.com-echo-crud/models"
	"poolsawat.com-echo-crud/routes"
)

func main() {
	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	users := map[int]*models.User{}

	route := &routes.RouteUserHandler{users}

	// Routes
	e.GET("/users", route.GetAllUsers)
	e.POST("/users", route.CreateUser)
	e.GET("/users/:id", route.GetUser)
	e.PUT("/users/:id", route.UpdateUser)
	e.DELETE("/users/:id", route.DeleteUser)

	// Start server
	e.Logger.Fatal(e.Start(":1323"))
}
  • ทดสอบ Service APIs ทั้งหมด
  • [POST] /users
curl -X POST \
  -H 'Content-Type: application/json' \
  -d '{"name":"Poolsawat.com 001"}' \
  localhost:1323/users
  • [GET] /users
curl localhost:1323/users
  • [GET] /users/1
curl localhost:1323/users/1
  • [PUT] /users/1
curl -X PUT \
  -H 'Content-Type: application/json' \
  -d '{"name":"Poolsawat.com 001 Updated"}' \
  localhost:1323/users/1
  • [DELETE] /users/1
curl -X DELETE localhost:1323/users/1
  • สุดท้ายอย่าลืมเขียน Test routes/user_test.go
package routes_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"poolsawat.com-echo-crud/routes"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
	"poolsawat.com-echo-crud/models"
)

func TestUsers(t *testing.T) {

	var (
		mockDB = map[int]*models.User{
			1: &models.User{1, "Poolsawat.com"},
		}
		extectedOne = "{\"id\":1,\"name\":\"Poolsawat.com\"}"
		extectedAll = "{\"1\":{\"id\":1,\"name\":\"Poolsawat.com\"}}"
	)

	t.Run(`should created user success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodPost, "/users", strings.NewReader(extectedOne))
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.CreateUser(c)) {
			assert.Equal(t, http.StatusCreated, rec.Code)
			assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should get all users success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodGet, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.GetAllUsers(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, extectedAll, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should get user by id success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodGet, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.GetUser(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, extectedOne, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should update user success`, func(t *testing.T) {
		// Given
		expected := "{\"id\":1,\"name\":\"Poolsawat.com update\"}"

		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodPut, "/users", strings.NewReader(expected))
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.UpdateUser(c)) {
			assert.Equal(t, http.StatusOK, rec.Code)
			assert.Equal(t, expected, strings.TrimSpace(rec.Body.String()))
		}
	})

	t.Run(`should delete user by id success`, func(t *testing.T) {
		// Setup
		e := echo.New()
		req := httptest.NewRequest(http.MethodDelete, "/users", nil)
		req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
		rec := httptest.NewRecorder()
		c := e.NewContext(req, rec)
		c.SetPath("/users/:id")
		c.SetParamNames("id")
		c.SetParamValues("1")

		mockRoute := &routes.RouteUserHandler{mockDB}

		// Assertions
		if assert.NoError(t, mockRoute.DeleteUser(c)) {
			assert.Equal(t, http.StatusNoContent, rec.Code)
			assert.Equal(t, "", strings.TrimSpace(rec.Body.String()))
		}
	})

}
  • รัน test ด้วย go test ./routes/user.go ./routes/user_test.go -v

ขอบคุณที่ติดตามครับ

Robot Framework แนะนำ Pycharm IDE ช่วยให้เขียน Robot ง่ายยิ่งขึ้น EP4

3 บทความก่อนหน้านี้ พาทุกคนทำความรู้จัก Robot มากยิ่งขึ้นไปแล้ว

และเมื่อก่อนที่จะเริ่มทำการพัฒนา Robot โปรเจคของเรา จำเป็นต้องเลือกหาเครื่องมือให้เหมาะสมกับผู้ใช้งานก่อน สำหรับในแวดวงการพัฒนาซอพแวร์ เครื่องมือสำหรับนักพัฒนามีมากมาย แต่เพราะด้วยความหลากหลายของภาษาโปรแกรมมิ่ง จะหาเครื่องมือที่ดีพอ และเหมาะสมกับภาษานั้น ๆ

เมื่อพูดถึงภาษา Python จะมีเครื่องมือสำหรับใช้เพื่อพัฒนาที่ได้รับความนิยม เช่น VScode, Pycharm,Jupyter Notebook, PyDev เป็นต้น แต่ทางผู้เขียนเลือกที่จะขอแนะนำ Pycharm เพราะการใช้งานเครืองมือจะคล้าย ๆ กับ intellij idea, eclipse ide ทำให้เลือกใช้เครื่องมือนี้

ข้อดีของการเลือกใช้ IDE แทนการเลือกใช้ Editor

  • ความสะดวกในการติดตั้ง ที่มาพร้อมกับ feature, plugin ต่าง ๆ ที่พร้อมใช้งาน
  • IDE จะมีฟีเจอร์ คำสั่งช่วยเหลือสำหรับการพิมพ์โค๊ด พร้อมกับคำอธิบายโค๊ดคำสั่ง
  • Pycharm จะมาพร้อมกับ python interpreter ช่วยสร้าง virtual environment สำหรับการจัดการ python libs ต่าง ๆ ค่อนข้างใช้งานง่าย
  • สามารถปรับแต่ง IDE Theme, Fonts, Colors หน้าตาของ IDE ได้
  • ปกติ IDE จะมาพร้อมกับ version control อย่าง GIT
  • ความสะดวกในการสั่งรันโปรแกรม IDE อย่าง pycharm ตรวจสอบหาไฟล์โค๊ดเพื่อรัน python จะเรียกหา function __main__

มาเริ่มทำความรู้จัก Pycharm IDE กัน

Pycharm IDE คือเครื่องมือที่ถูกพัฒนาจากทีมงาน Jetbrains.com ที่เป็นทีมงานสร้าง IDE ดัง ๆ อย่าง IntelliJ IDEA, PhpStorm และอื่น ๆ อีกมากมาย เป็นเหตุผลนึงที่ทำให้ผู้เขียนเลือกที่จะใช้ pycharm เพื่อใช้พัฒนา Robot project

หลังจากดาวน์โหลดและทำการติดตั้ง Pycharm เป็นที่เรียบร้อยผู้เขียนจะขอแนะนำการกำหนดการตั้งค่า ที่คิดว่าจำเป็นสำหรับการโค๊ด Robot project

การเริ่มสร้าง Project ใหม่

  • เลือกเมนู File -> new Project…

Localtion : พื้นที่จัดเก็บไฟล์ project ที่ถูกสร้างใหม่นี้
Python Interpreter: New Virtualenv environment : เลือก New environment using
– Virtualenv (เลือกอันนี้)
– Pipenv
– Portry
– Conda
จากนั้นกด Create ก็เป็นอันเสร็จการสร้าง Project

อธิบาย Source Exploer ของ Project

มุมมองการแสดงไฟล์
– Project : จะแสดงเฉพาะไฟล์เกี่ยวกับโค๊ดเท่านั้น สามารถเปิดดูไฟล์ได้ง่าย มีการแบ่งชัดเจน
– Project Files : แสดงไฟล์โค๊ด และอื่น ๆ
– Open Files : แสดงไฟล์เฉพาะที่เปิดแท๊บการใช้งาน
– All Changed Files : แสดงไฟล์ที่เปลี่ยนแปลง Git change
– Scatches and Colsoles : ยังไม่ทราบว่าใช้งานอย่างไร

การตั้งค่า Virtualenv ปรับเปลี่ยน environment อื่น ๆ

  • เลือกเมนู File -> Settings… -> Project
  • เลือกเมนู Python Interpreter -> Add…
  • เลือก New environment
    • Location : พื้นที่เก็บไฟล์ environment
    • Base interpreter : python compiler กรณีมี python มากกว่า 1 เวอร์ชั่น

เรียกใข้งาน Terminal ภายใน pycharm และเลือกใช้งานจาก Python Interpreter ที่สร้างใหม่

  • เลือกแท็บ Terminal จะแสดง Terminal -> Local
  • poolsawat.com-venv1 : ชื่อ virtualenv ที่สร้างขี้นใหม่

สั่งรันง่าย ๆ เลือกไฟล์และก็รัน

  • main function เพื่อรัน
  • Edit Configuations…
  • Script path : ตำแหน่งไฟล์เพื่อสั่งรัน
  • Python interpreter : สามารถเลือกปรับเปลี่ยนได้

ติดตั้ง libs ง่ายๆ จากไฟล์ requiments.txt

  • Pycharm จะแสดง Install requirements ถ้าเกิดว่า dependencies ยังไม่ถูกติดตั้ง

EP1 Robot Framework เตรียมพร้อมก่อนเริ่มโค๊ด Robot EP1
EP2 Robot Framework เริ่มต้น กำหนดโครงสร้างโปรเจค EP2
EP3 Robot Framework อธิบายการทำงาน EP3

Robot Framework อธิบายการทำงาน EP3

ต่อจากบทความ Robot Framework เริ่มต้น กำหนดโครงสร้างโปรเจค EP2 ที่แนะนำการกำหนดโครงสร้างของโปรเจค และวิธีการรัน testcases ที่สร้าง มาต่อกันที่บทความนี้จะขอธิบายรายละเอียดของ โฟลเดอร์ ไฟล์ และข้อมูลอื่น ๆ ที่จะช่วยสนับสนุนการพัฒนา Robot project ให้รวดเร็วและดูเป็นระบบ ยิ่งขึ้น

ทำความเข้าใจ รายละเอียด ต่าง ๆ ของโค๊ด Robot

บทความนี้จะข้อใช้ตัวอย่างโค๊ดจาก github นี้ ซึ่งทุกคนสามารถ clone ไปเพื่อประกอบกับการอ่านบทความนี้

  • poolsawat.com (ชื่อโปรเจค)
    • keywords (ไฟล์ keywords ต่าง ๆ )
      • api_keywords.robot
    • pythonlibs (function ต่าง ๆ สามารถเรียกใช้งานเหมือนกับ keywords ปกติ)
      • date_util.py
    • resources (ไฟล์ data, config, data test)
      • configs
        • dev
          • env.yaml
        • staging
          • env.yaml
      • testdata
        • dev
          • example_data.yaml
        • staging
          • example_data.yaml
      • commons.yaml
      • imports.robot
    • scripts
    • testcases (ไฟล์ testcases ต่าง ๆ)
      • module1_testcase.robot
    • venv
    • requirements.txt

/keywords/api_keywords.robot

*** Keywords *** (1)
Print Message from agruments (2)
    [Documentation]    Print Message from agruments  (3)
    [Arguments]    ${arg0}   (4)
    Log To Console    'Result from Print Message from agruments keywords ::=='${arg0}    (5)

Calculate value
    [Documentation]    Calculate value
    [Arguments]    ${a}     ${b}
    ${result}=      Evaluate     ${a}+${b}   (6)
    [Return]        ${result}     (7)

อธิบายแต่ละบรรทัดของโค๊ด ของไฟล์ api_keywords.robot

(1) *** Keywords *** เพื่อเริ่มต้นสร้าง keywords ใหม่
(2) กำหนดชื่อของ keyword สำหรับการทำงาน ปกติเราจะอ้างอิงการเรียกใช้งานด้วยชื่อ ยาว ๆ นี้ เช่น Print Message from agruments เป็นต้น
(3) คำอธิบายรายะเอียดของ keyword นี้ เป็น optional คือจะกำหนด หรือไม่มีก็ได้ สามารถพิ่มรายละเอียดได้
(4) กำหนด parameters ต่าง ๆ ตัวอย่างจะเป็น ${arg0} ตัวอย่างการเรียกใช้งาน Print Message from agruments arg0=’Hello world’
(5) กำหนด logical การทำงานต่าง ๆ ตัวอย่างจะ print message กับ parameter ชื่อ arg0 เพื่อแสดงใน log ด้วย keywords Log To Console
(6) keyword Evaluate จะช่วยประเมิน การประมวลผล เพื่อเก็บข้อมูลใส่ ${result}
(7) สามารถ return data คืนให้กับส่วนที่เรียก keyword นี้

pythinlibs/date_util.py

from datetime import datetime

def get_nowdate(): (1)
    return datetime.now()

อธิบายแต่ละบรรทัดของโค๊ด ของไฟล์ date_util.py

(1) สร้าง function (จะถูกเรียกใช้งานแบบเดียวกับ keyword) เช่น get_newdate

resources/configs/${ENV}/env.yaml

welcome_message: welcome from Dev environment

อธิบายการเรียกใช้งานไฟล์ตาม environment

${ENV} จะเป็นค่า variable ที่ถูกกำหนดตั้งแต่ตอนสั่งรันด้วย

robot -L TRACE --variable ENV:dev -t "CASE_000**" "testcases/episode_1.robot"

จะเรียกใช้ resorce จาก dev เมื่อส่ง ENV:dev และเรียก staging เมื่อส่ง ENV:staging

resources/testdata/$ENV{}/example_data.yaml

${ENV} จะเป็นค่า variable ที่ถูกกำหนดตั้งแต่ตอนสั่งรัน แบบเดียวกับตัวอย่าง config ก่อนหน้า

resources/commons.yaml

app_name: poolsawat.com

คล้ายกับ config ที่แตกด้วย ENV แต่ resource common จะเป็น config แบบไม่ได้แยกตาม environment เป็นค้าคงที่ ที่ทุก environment ใช้แบบเดียวกัน

resources/imports.robot

*** Settings ***  (1)
Library    Collections   (2)
Library    String    (3)
Library    DateTime   (4)
Library    ../pythonlibs/date_util.py   (5)

Variables   ./commons.yaml    (6)
Variables    ../resources/configs/${ENV}/env.yaml     (7)

Resource    ../keywords/api_keywords.robot    (8)

เป็นไฟล์ control เรื่องการ import resource ต่าง ๆ เป็นการ refactor code แบบนึง เป็นการย้าย code การ import resource ทุก ๆ ไฟล์ testcase ให้มา control ที่ไฟล์ imports.robot นี้ไฟล์เดียว จะทำให้เราจัดการเรื่องการ control การ improt libs, resources, testdata, config ต่าง ๆ ได้ง่ายยิ่งขึ้น

อธิบายแต่ละบรรทัดของโค๊ด ของไฟล์ imports.robot

(1) กำหนดเริ่มต้นเมื่อมีการเรียกใช้การ import files, libs, resources ต่างๆ
(2),(3),(4) import libs ที่ชื่อว่า Collections, String, Datetime ซึ่งเป็น standard libs จะมากับ robot system อยู่แล้ว โดยจะกำหนดการ import เป็นแบบ Library
(5) import custom function ใช้การ import แบบ Library เช่นกัน
(6),(7) import variable data จะกำหนดต่างจาก Library keyword เพราะไฟล์เหล่านี้เป็นแค่ data file จะถูกทองเป็น Variables
(8) สุดท้าย keywords file จะถูก import แบบ Resource file

testcases/episode_1.robot

*** Settings ***  (1)
Resource    ../resources/imports.robot     (2)
Resource    ../keywords/api_keywords.robot     (3)
Variables    ../resources/testdata/${ENV}/example_data.yaml     (4)

*** Variables ***   (5)
${DIRECTORY_CSV}       ../resources/testdata/csv     (6)

*** Test Cases ***   (7)
CASE_00001 lesson 1 Log to console     (8)
    Log To Console    'Hello World 1'     (9)

CASE_00002 lesson 2 use external keywords with void
    Print Message from agruments  'Hello world 2'

CASE_00003 lesson 3 use external keywords with return
    ${result}=     Calculate value   5     10
    Log To Console    'Result from Calculate value keywords::=='${result}

CASE_00004 lesson 4 get data from yaml file
    Log To Console   'request ::='${test_data.CASE_00004.request}
    Log To Console   'expect_data ::=='${test_data.CASE_00004.expect_data}

CASE_00005 lesson 5 get env global file
    Log To Console   'app_name ::=='${app_name}

CASE_00006 lesson 6 use custom lib keywords
    ${now}=     get_nowdate
    Log To Console   'now ::=='${now}

CASE_00007 lesson 7 use variable with resource environnment
    Log To Console   'welcome_message from ${ENV}::=='${welcome_message}

อธิบายแต่ละบรรทัดของโค๊ด ของไฟล์ episode_1.robot

(1) *** Settings *** จะถูกกำหนดเมื่อมีการ import file, resource data ต่าง ๆ
(2) import file imports.robot ที่ได้ทำการ control lib ต่าง ๆ ไว้แล้ว
(3) import custom keywords เข้ามาใช้งาน เพื่อรอการเรียกใช้งาน keywords ภายในไฟล์ต่อไป
(4) import testdata file สังเกตุว่าจะมีการระบุการใช้งานโดยแยก environment ด้วย /${ENV}/ ทำให้เราสามารถแยก testdata ตาม environment ได้
(5) *** Variables *** เมื่อมีการ define new variable จำเป็นต้องประกาศ *** Variables *** ก่อนเสมอ
(6) สร้าง variable ที่ชื่อ ${DIRECTORY_CSV} พร้อม assign ค่าให้
(7) *** Test Cases *** จะเป็นการเริ่มการสร้าง testcase ของเราบรรทัดของโคํดที่อยู่ภายใต้นี้จะมองเป็น keywords testcase ทั้งหมด

Resource Folders, Files อื่น ๆ

  • venv จะเป็น folder เก็บ libs, resource ต่าง ๆ สำหรับการพัฒนาโปรเจคด้วย python จะนิยมใช้ virtual env
  • log.html จะเป็น summary report ของการรัน testcase ต่าง ๆ
  • requirements.txt ไฟล์ control libs version ต่าง ๆ ปกติจะ control libs กันด้วยไฟล์นี้

EP1 Robot Framework เตรียมพร้อมก่อนเริ่มโค๊ด Robot EP1
EP2 Robot Framework เริ่มต้น กำหนดโครงสร้างโปรเจค EP2
EP4 Robot Framework แนะนำ Pycharm IDE ช่วยให้เขียน Robot ง่ายยิ่งขึ้น EP4

Robot Framework เริ่มต้น กำหนดโครงสร้างโปรเจค EP2

ต่อจากบทความ Robot Framework เตรียมพร้อมก่อนเริ่มโค๊ด Robot EP1 จะแนะนำ scripts ของ robot ที่จำเป็นต้องทราบก่อนการเพิ่มโค๊ด Robot มาต่อกันที่บทความนี้ขจะพาทุกคน เริ่มสร้าง กำหนดโครงสร้างโฟลเดอร์ ไฟล์ ต่างๆ ของ โปรเจค

คำแนะนำเกี่ยวกับข้อมูลและเนื้อหาของบทความนี้

  • ติดตั้ง python 3.8 ขึ้นไป
  • ติดตั้ง pycharm

กำหนดโครงสร้างโฟลเดอร์ ไฟล์ ต่างๆ ภายในโปรเจค

ตัวอย่างนี้เป็นเพียงการออกแบบจากผู้เขียนบทความเอง เท่านั้น ผู้อ่านสามารถปรับเปลี่ยน แก้ไข เพื่อให้เหมาะสม

  • poolsawat.com (ชื่อโปรเจค)
    • keywords (ไฟล์ keywords ต่าง ๆ )
      • ***_keywords.robot
    • pythonlibs (function ต่าง ๆ สามารถเรียกใช้งานเหมือนกับ keywords ปกติ)
      • ***.py
    • resources (ไฟล์ data, config, data test)
      • configs
        • dev
          • env.yaml
        • staging
          • env.yaml
      • testdata
        • dev
          • module1_data.yaml
        • staging
          • module1_data.yaml
      • commons.yaml
      • imports.robot
    • scripts
    • testcases (ไฟล์ testcases ต่าง ๆ)
      • module1_testcase.robot
    • venv
    • requirements.txt

ตัวอย่างคำสั่ง รัน testcase และ ผลลัพธ์จากการ รัน testcase

command เพื่อใช้สั่ง run testcase

robot -L TRACE --variable ENV:dev -t "CASE_000**" "testcases/episode_1.robot"
  • robot : คำสั่งจะสามารถเรียกใช้งานได้หลังจากติดตั้ง robotframework
  • -L TRACE : write log level TRACE
  • –variable ENV:dev : defined variable ENV เพื่อใช้แยก resource environment ในโค๊ด Robot
  • -t “CASE_000**” : filter cases ที่ต้องการสั่งรัน เป็นชุด testcase เพื่อใช้ในการรัน
  • “testcases/episode_1.robot” : specific file testcase ที่จะรัน
(poolsawat.com-venv1) D:\RobotFramework\poolsawat.com>robot -L TRACE --variable ENV:dev -t "CASE_000**" "testcases/episode_1.robot"
==============================================================================
Episode 1                                                                     
==============================================================================
CASE_00001 lesson 1 Log to console                                    'Hello World 1'
CASE_00001 lesson 1 Log to console                                    | PASS |
------------------------------------------------------------------------------
CASE_00002 lesson 2 use external keywords with void                   'Result from Print Message from agruments keywords ::==''Hello world 2'
CASE_00002 lesson 2 use external keywords with void                   | PASS |
------------------------------------------------------------------------------
CASE_00003 lesson 3 use external keywords with return                 .'Result from Calculate value keywords::=='15
CASE_00003 lesson 3 use external keywords with return                 | PASS |
------------------------------------------------------------------------------
CASE_00004 lesson 4 get data from yaml file                           'request ::='{'name': 'poolsawat Dev Env', 'age': 30, 'nation': 'Thai'}
.'expect_data ::=='{'status': {'code': 'API200', 'message': 'Success'}}
CASE_00004 lesson 4 get data from yaml file                           | PASS |
------------------------------------------------------------------------------
CASE_00005 lesson 5 get env global file                               'app_name ::=='poolsawat.com
CASE_00005 lesson 5 get env global file                               | PASS |
------------------------------------------------------------------------------
CASE_00006 lesson 6 use custom lib keywords                           .'now ::=='2022-07-13 14:47:23.480131
CASE_00006 lesson 6 use custom lib keywords                           | PASS |
------------------------------------------------------------------------------
CASE_00007 lesson 7 use variable with resource environnment           'welcome_message from dev::=='welcome from Dev environment
CASE_00007 lesson 7 use variable with resource environnment           | PASS |
------------------------------------------------------------------------------
Episode 1                                                             | PASS |
7 tests, 7 passed, 0 failed
==============================================================================
Output:  D:\RobotFramework\poolsawat.com\output.xml
Log:     D:\RobotFramework\poolsawat.com\log.html
Report:  D:\RobotFramework\poolsawat.com\report.html

และสามารถเปิดตรวจสอบ result ได้จากไฟล์ .\log.html

บทความถัดไปจะเป็นเนื้อหาที่เน้นลงลึกไปในรายละเอียดของไฟล์ต่าง ๆ ขอบคุณที่ติดตาม

EP1 Robot Framework เตรียมพร้อมก่อนเริ่มโค๊ด Robot EP1
EP3 Robot Framework อธิบายการทำงาน EP3
EP4 Robot Framework แนะนำ Pycharm IDE ช่วยให้เขียน Robot ง่ายยิ่งขึ้น EP4

Robot Framework เตรียมพร้อมก่อนเริ่มโค๊ด Robot EP1

Robot Framework เครื่องมือสำหรับช่วยพัฒนา application ที่ได้รับความนิยมในระดับนึง เพราะด้วยความเป็น opensource และง่ายต่อการนำไปใช้งาน จึงทำให้ Robot Framework ยังได้รับความนิยมจนถึงปัจจุบัน

Robot Framework ถูกพัฒนาจากภาษาโปรแกรมมิ่ง Python ฉะนั้นเมื่อนำไปใช้งาน จำเป็นต้องติดตั้ง Python ให้เรียบร้อยก่อนการเริ่มใช้งาน อีกทั้งความเป็น keyword approach กล่าวคือ keyword (function, method แล้วแต่จะเรียก) มีความเป็นภาษาอ่านที่เข้าใจง่าย

แนะนำ Scripts ของ Robot ที่จำเป็นต้องรู้

  • Settings (*** Settings ***) เรียกใช้ Library ต่าง ๆ เช่น
*** Settings ***
Library    Collections
Library    String
Library    DateTime
Library    ../pythonlibs/date_util.py

Variables   ./commons.yaml
Variables    ../resources/configs/${ENV}/env.yaml

Resource    ../keywords/api_keywords.robot
  • Variables (*** Variables ***) สร้างกำหนด Variable global scope
*** Variables ***
${DIRECTORY_CSV}       ../resources/testdata/csv
  • Test Cases (*** Test Cases ***) กำหนดสร้าง testcases ต่าง ๆ ที่จำใช้ทดสอบระบบ
*** Test Cases ***
CASE_00001 lesson 1 Log to console
    Log To Console    'Hello World 1'

CASE_00002 lesson 2 use external keywords with void
    Print Message from agruments  'Hello world 2'

CASE_00003 lesson 3 use external keywords with return
    ${result}=     Calculate value   5     10
    Log To Console    'Result from Calculate value keywords::=='${result}

CASE_00004 lesson 4 get data from yaml file
    Log To Console   'request ::='${test_data.CASE_00004.request}
    Log To Console   'expect_data ::=='${test_data.CASE_00004.expect_data}

CASE_00005 lesson 5 get env global file
    Log To Console   'app_name ::=='${app_name}

CASE_00006 lesson 6 use custom lib keywords
    ${now}=     get_nowdate
    Log To Console   'now ::=='${now}

CASE_00007 lesson 7 use variable with resource environnment
    Log To Console   'welcome_message from ${ENV}::=='${welcome_message}
  • Keywords (*** Keywords ***) กำหนด keywords ของเรา เหมือนการสร้าง function ของ Javascript หรือ method ของภาษา Java
*** Keywords ***
Print Message from agruments
    [Documentation]    Print Message from agruments
    [Arguments]    ${arg0}
    Log To Console    'Result from Print Message from agruments keywords ::=='${arg0}

Calculate value
    [Documentation]    Calculate value
    [Arguments]    ${a}     ${b}
    ${result}=      Evaluate     ${a}+${b}
    [Return]        ${result}

4 scripts ที่แนะนำมา จะค่อนข้างใช้งานบ่อย ๆ ที่สุด สำหรับ EP ถัดไปจะพาทุกคนมาเริ่มวางโครงสร้างของ Project เพิ่มเริ่มการพัฒนาด้วย Robot Framework กันครับ

EP2 Robot Framework เริ่มต้น กำหนดโครงสร้างโปรเจค EP2
EP3 Robot Framework อธิบายการทำงาน EP3
EP4 Robot Framework แนะนำ Pycharm IDE ช่วยให้เขียน Robot ง่ายยิ่งขึ้น EP4

Golang Program Flow Control EP5

Golang ก็เหมือนภาษาอื่น ๆ คือจะมี IF, ELSE, FOR LOOPS, SWITH CASE อื่น ๆ

IF, ELSE IF, ELSE คำสั่งหากินของทุก ๆ ภาษา เกือบจะถูกเรียกว่า AI

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้

// if condition_that_evaluates_to_boolean{
//      perform action1
// }else if condition_that_evaluates_to_boolean{
//      perform action2
// }else{
//      perform action3
// }

price, inStock := 100, true

if price >= 80 { // parenthesis are no required to enclose the testing condition
    fmt.Println("Too Expensive")
}

if price <= 100 && inStock == true { //the same with: if price <= 100 && inStock { }
    fmt.Println("Buy it!")
}

// In Go there is not such a thing like the Truthiness of a variable.
// Error:
// if price {
//  fmt.Println("We have price!")
// }

// only one if branch will be executed
if price < 100 {
    fmt.Println("It's cheap!")
} else if price == 100 {
    fmt.Println("On the edge")
} else { //executed only once if all the if branches are false (it's optional)
    fmt.Println("It's Expensive!")
}

Simple IF

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้ ง่ายดี

package main
 
import (
    "fmt"
    "strconv"
)
 
func main() {
 
    // converting string to int:
    i, err := strconv.Atoi("45")
 
    // error handling
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(i)
 
    }
 
    // simple (short) statement ->  the same effect as the above code
    // i and err are variables scoped to the if statement only
    if i, err := strconv.Atoi("34"); err == nil {
        fmt.Println("No error. i is ", i)
    } else {
        fmt.Println(err)
    }
}

For Loops การวนทำ ตามจำนวนข้อมูล

อธิบายรวดเดียวกันไปเลย ด้วยโค๊ดข้างล่างนี้ ง่ายดี


package main
 
import "fmt"
 
func main() {
 
    // printing numbers from 0 to 9
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
 
    // has the same effect as a while loop in other languages
    // there is no while loop in Go
    j := 10
    for j >= 0 {
        fmt.Println(j)
        j--
    }
 
    // handling of multiple variables in a for loop
    for i, j := 0, 100; i < 10; i, j = i+1, j+1 {
        fmt.Printf("i = %v, j = %v\n", i, j)
    }
 
    // infinite loop
    // sum := 0
    // for {
    //  sum++
    // }
    // fmt.Println(sum) //this line is never reached
}

หากต้องการที่จะ break (หยุดการทำงาน) หรือ continue (ต่อเนื่อง จะข้ามรอบการทำงานนั้น) จะทำอย่างไร

 //** CONTINUE STATEMENT **//
 
// It works just the same as in C,  Java or Python.
// The continue statement rejects all the remaining statements in the current iteration of the loop
// and moves the control back to the top of the loop.


// printing even numbers less than or equal to 10
for i := 1; i <= 10; i++ {
    if i%2 != 0 {
        continue    // skipping the remaining code in this iteration
    }
    fmt.Println(i)
}


// **BREAK STATEMENT **//

// It is used to terminate the innermost for or switch statement.
// It works just the same as in C,  Java or Python.

// finding 10 numbers divisible by 13 
count := 0 
for i := 0; true; i++ {
    if i%13 == 0 {
        fmt.Printf("%d is divisible by 13\n", i)
        count++
    }

    if count == 10 { //if 10 numbers were found, break!
        break //it breaks the current loop (inner loop if there are more loops)
    }
}

// the break statement is not terminating the program entirely;
fmt.Println("Just a message after the for loop")

จะลืมได้ไง Switch Statement

package main
 
import "fmt"
 
func main() {
 
    language := "golang"
 
    switch language {
    case "Python": //values must be comparable (compare string to string)
        fmt.Println("You are learning Python! You don't use { } but indentation !! ")
        // an implicit break is added here
    case "Go", "golang": //compare language with "Go" OR "golang"
        fmt.Println("Good, Go for Go!. You are using {}!")
    default:
        // the default clause the equivalent of the else clause of an if statement
        // and gets executed if no testing condition is true.
        fmt.Println("Any other programming language is a good start!")
    }
 
    n := 5
    // comparing the result of an expression which is bool to another bool value
    switch true {
    case n%2 == 0:
        fmt.Println("Even!")
    case n%2 != 0:
        fmt.Println("Odd!")
    default:
        fmt.Println("Never here!")
    }
 
    //** Switch simple statement **//
 
    // Syntax: statement (n:=10), semicolon and a switch condition
    //(true in this case, we are comparing boolean expressions that return true)
    // we can remove the word "true" because it's the default
    switch n := 10; true {
    case n > 0:
        fmt.Println("Positive")
    case n < 0:
        fmt.Println("Negative")
    default:
        fmt.Println("Zero")
    }
}

Golang Data Types และ Operators EP4

Data Types ก็คล้าย ๆ เหมือนภาษาอื่น ๆ

  • NUMERIC TYPES ภายใต้ type นี้จะย่อยอีกเยอะมาก อธิบายตามโค๊ดข้างล่าง
    • uint เก็บค่าตั้งแต่จำนวนที่เป็นบวก รวมถึง 0
    • int เก็บค่า เป็นลบ 0 และบวก
    • float เก็บค่าที่รูปแบบทศนิยม
    • byte นามแผงของ uint8
    • rune นามแผงของ int32
// uint8      the set of all unsigned  8-bit integers (0 to 255)
// uint16      the set of all unsigned 16-bit integers (0 to 65535)
// uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
// uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

// int8        the set of all signed  8-bit integers (-128 to 127)
// int16       the set of all signed 16-bit integers (-32768 to 32767)
// int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
// int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

// uint     either 32 or 64 bits
// int      same size as uint

// float32     the set of all IEEE-754 32-bit floating-point numbers
// float64     the set of all IEEE-754 64-bit floating-point numbers
// complex64   the set of all complex numbers with float32 real and imaginary parts
// complex128  the set of all complex numbers with float64 real and imaginary parts

// byte        alias for uint8
// rune        alias for int32

ตัวอย่างโค๊ด Numeric Types รูปแบบต่าง ๆ

//int type
var i1 int8 = -128     //min value
fmt.Printf("%T\n", i1) // => int8

var i2 uint16 = 65535  //max value
fmt.Printf("%T\n", i2) // => int16

var i3 int64 = -324_567_345  // underscores are used to write large numbers for a better readability
fmt.Printf("%T\n", i3)       // => int64
fmt.Printf("i3 is %d\n", i3) // => i3 is -324567345 (underscores are ignored)

//float64 type
var f1, f2, f3 float64 = 1.1, -.2, 5. // trailing and leading zeros can be ignored
fmt.Printf("%T %T %T\n", f1, f2, f3)

//rune type
var r rune = 'f'
fmt.Printf("%T\n", r) // => int32 (rune is an alias to int32)
fmt.Printf("%x\n", r) // => 66,  the hexadecimal ascii code for 'f'
fmt.Printf("%c\n", r) // => f
  • bool type เป็นได้แค่ true, false
  • string ข้อความ ต่างๆ หลาย ๆ ตัวอักษรรวมเป็น string
//bool type
var b bool = true
fmt.Printf("%T\n", b) // => bool

//string type
var s string = "Hello Go!"
fmt.Printf("%T\n", s) // => string

Array vs Slice Types

  • Array เป็น Data Type ที่กำหนดขนาดชัดเจน กำหนด size
  • Slice เป็น Data Type ที่ไม่กำหนดขนาด
//array type
var numbers = [4]int{4, 5, -9, 100}
fmt.Printf("%T\n", numbers) // =>  [4]int

//slice type
var cities = []string{"London", "Bucharest", "Tokyo", "New York"}
fmt.Printf("%T\n", cities) // => []string

Map การเก็บค่าข้อมูลโดยการกำหนด Key: Value

  • Key ของ Map จะ unique ไม่มีทางซ้ำกันได้
  • Value สามารถกำหนดได้ โดยทุก elements จะต้องเป็น Data Type แบบเดียวกัน
//map type
balances := map[string]float64{
    "USD": 233.11,
    "EUR": 555.11,
}
fmt.Printf("%T\n", balances) // => map[string]float64

Struct การกำหนดรูปแบบที่คล้ายการทำ Model, JPA Class ในภาษา Java

  • สามารถกำหนดตั้งชื่อได้เอง กำหนด properties ได้ไม่จำกัด แต่ละ property จะกำหนดด้วย Data Types ที่แตกต่างกันได้
//struct type
type Person struct {
    name string
    age  int
}
var you Person
fmt.Printf("%T\n", you) // => main.Person

Pointer Type เรื่องที่เข้าใจยาก ในบรรดาทุก Data Types

  • pointer คือตัวชี้ตำแหน่ง ถ้าใน GO ก็จะเป็นตำแหน่งการเก็บข้อมูลโดยจะไม่สามารถซ้ำๆ กับ address อื่น ได้
  • จะใช้สัญลักษณ์ * เพื่อบอกให้รู้ว่ากำลังกำหนด Type แบบ Pointer
  • โดยจะใช้ควบคู่กับ address ที่จะเป็นการบอกให้รู้ถึงที่อยู่ของ pointer นั้น ๆ จะใช้สัญลักษณ์ &
//pointer type
var x int = 2
ptr := &x                                                 // pointer to int
fmt.Printf("ptr is of type %T with value %v\n", ptr, ptr) // => ptr is of type *int with value 0xc000016168

Function Type

  • GO จะถือว่า function เป็น type รูปแบบนึงคล้าย javascript
//function type
fmt.Printf("%T\n", f) // => func()

func f() {
}

Operators อธิบายสั้น ๆ ด้วยโค๊ดข้างล่าง

a, b := 10, 5.5

//** ARITHMETIC OPERATORS **//
//  +       sum
// -        difference
// *        product
// /        quotient
// %        remainder
// there is no power operator in Go. Use math.Pow(a, b) for raising to a power.

fmt.Println(a + 5)   // => 15
fmt.Println(3.1 - b) // => -2.4
fmt.Println(a * a)   // => 100
fmt.Println(a / a)   // => 1
fmt.Println(11 / 5)  // => 2

// Go is a Strong Typed Language
// fmt.Println(a * b)       // =>  invalid operation: a * b (mismatched types int and float64)
fmt.Println(a * int(b))     // => 50
fmt.Println(float64(a) * b) // => 55

// IncDec Statements
// The "++" and "--" statements increment or decrement their operands by the untyped constant 1.
x := 10
x++ // x is 11. Same as: x += 1
x-- // x is 10. Same as: x -= 1

//** ASSIGNMENT OPERATORS **//
//  =   (simple assignment)
// +=   (increment assignment)
// -=   (decrement assignment)
// *=   (multiplication assignment)
// /=   (division assignment)
// %=   (modulus assignment)

a = 10
a += 2 // => a is 12
a -= 3 // => a is 9
a *= 2 // => a is 18
a /= 3 // => a is 6
a %= 5 // => a is 1

//** COMPARISON OPERATORS **//
//  ==      equal values
// !=       not equal
// >        left operand is greater than right operand
// <        left operand is less than right operand
// >=       left operand is greater than or equal to right operand
// <=       left operand is less than or equal to right operand

fmt.Println(5 == 6)   // => false
fmt.Println(5 != 6)   // => true
fmt.Println(10 > 10)  // => false
fmt.Println(10 >= 10) // => true
fmt.Println(5 < 5)    // => false
fmt.Println(5 <= 5)   // => true

//** LOGICAL OPERATORS **//
// &&       logical and
// ||       logical or
// !        logical negation

fmt.Println(0 < 2 && 4 > 1) // => true
fmt.Println(1 > 5 || 4 > 5) // => false
fmt.Println(!(1 > 2))       // => true

Converting Types

  • การแปลง Type ของ Data นึง ไปเป็นอีก Type Data นึง เช่น int -> string, string -> int
var x = 3   //int type
var y = 3.2 //float type

// x = x * y //compile error ->  mismatched types

x = x * int(y) // converting float64 to int
fmt.Println(x) // => 9

y = float64(x) * y //converting int to float64
fmt.Println(y)     // => 28.8

x = int(float64(x) * y)
fmt.Println(x) // => 259

//In Go types with different names are different types.
var a int = 5   // same size as int64 or int32 (platform specific)
var b int64 = 2 // int and int64 are not the same type

// a = b // error: cannot use b (type int64) as type int in assignment
a = int(b) // converting int64 to int (explicit conversion required)

// preventing unused variable error
_ = a

//** CONVERTING NUMBERS TO STRINGS AND STRINGS TO NUMBERS **//

s := string(99)            // int to rune (Unicode code point)
fmt.Println(s)             // => 99, the ascii code for symbol c
fmt.Println(string(34234)) // => 34234 is the unicode code point for 薺

// we cannot convert a float to a string similar to an int to a string
// s1 := string(65.1) // error

// converting float to string
var myStr = fmt.Sprintf("%f", 5.12)
fmt.Println(myStr) // => 5.120000

// converting int to string
var myStr1 = fmt.Sprintf("%d", 34234)
fmt.Println(myStr1) // => 34234

// converting string to float
var result, err = strconv.ParseFloat("3.142", 64)
if err == nil {
    fmt.Printf("Type: %T, Value: %v\n", result, result) // => Type: float64, Value: 3.142
} else {
    fmt.Println("Cannot convert to float64!")
}

// Atoi(string to int) and Itoa(int to string).
i, err := strconv.Atoi("-50")
s = strconv.Itoa(20)
fmt.Printf("i Type is %T, i value is %v\n", i, i) // => i Type is int, i value is -50
fmt.Printf("s Type is %T, s value is %q\n", s, s) // => s Type is string, s value is "20"

Defined Types กำหนดชื่อ Type ใหม่

  • กำหนดชื่อของ Type ที่สื่อความหมายเฉพาะ
package main
 
import "fmt"
 
type age int        //new type, int is the underlying type
type oldAge age     //new type, int (not age) is the underlying type
type veryOldAge age //new type, int (not age) is the underlying type
 
func main() {
 
    // new type speed (underlying type uint)
    type speed uint
 
    // s1, s2 of type speed
    var s1 speed = 10
    var s2 speed = 20
 
    // performing operations with the new types
    fmt.Println(s2 - s1) // -> 10
 
    // uint and speed are different types (they have different names)
    var x uint
 
    // x = s1  //error different types
 
    // correct
    x = uint(s1)
    _ = x
 
    // correct
    var s3 speed = speed(x)
    _ = s3
}

Golang ทำไม package fmt ถึงสำคัญสำหรับผู้เริ่มต้น EP3

package fmt คืออะไร

ใช้ I/O ที่จัดรูปแบบด้วยฟังก์ชันที่คล้ายคลึงกับ printf และ scanf ของภาษา C รูปแบบการกร มาจาก C แต่ง่ายกว่า

fmt.Println

  • การ Print แบบขึ้นบรรทัดใหม่ ด้วย fmt.Println(“message”) //=> message
  • การ Print หลาย args แบบขึ้นบรรทัดใหม่ fmt.Println(“arg0 := “,10,”, arg1 := “,99) // => arg0 := 10, arg1 := 99
 fmt.Println("Hello Go World!") // => Hello Go World!

fmt.Printf

  • การ Print ที่จะสามารถกำหนด format ของ message ได้โดย format ที่กำหนดมีอยู่ด้วยกันหลายรูปแบบ เช่น
    • %d -> decimal
    • %f -> float
    • %s -> string
    • %q -> double-quoted string
    • %v -> value (any)
    • %#v -> a Go-syntax representation of the value
    • %T -> value Type
    • %t -> bool (true or false)
    • %p -> pointer (address in base 16, with leading 0x)
    • %c -> char (rune) represented by the corresponding Unicode code poi
 fmt.Printf()

ตัวอย่างการใช้งาน fmt.Printf

a, b, c := 10, 15.5, "Gophers"
grades := []int{10, 20, 30}
 
fmt.Printf("a is %d, b is %f, c is %s \n", a, b, c)    // => a is 10, b is 15.500000, c is Gophers
fmt.Printf("%q\n", c)                      // => "Gophers"
fmt.Printf("%v\n", grades)                 // => [10 20 30]
fmt.Printf("%#v\n", grades)                // => b is of type float64 and grades is of type []int
fmt.Printf("b is of type %T and grades is of type %T\n", b, grades) 
    // => b is of type float64 and grades is of type []int
fmt.Printf("The address of a: %p\n", &a)    // => The address of a: 0xc000016128
fmt.Printf("%c and %c\n", 100, 51011)       // =>  d and 읃  (runes for code points 101 and 51011)

แต่ถ้าอยากที่จะขึ้นบรรทัดใหม่ด้วยก็ให้ เพิ่ม \n ใน fmt.Printf(“%q\n”, c) ก็จะได้การขึ้นบรรทัดใหม่ด้วย

fmt.Sprintf

ใช้งานเหมือน Printf แต่จะ return เป็น string

a, b, c := 10, 15.5, "Gophers"
// fmt.Sprintf() returns a string. Uses the same verbs as fmt.Printf()
s := fmt.Sprintf("a is %d, b is %f, c is %s \n", a, b, c)
fmt.Println(s) // => a is 10, b is 15.500000, c is Gophers

Golang เรียนนรู้ Syntax ต่าง ๆ ของ GO EP2

Variables and Declarations

ตัวแปร และการประกาศค่า หลัก ๆ
  • ที่เห็นได้ชัดเจน จะมี 2 รูปแบบ คือกำหนด type ชัดเจน เช่น var s1 string และให้แปลง type ให้ตามข้อมูลที่ assign เริ่มต้น เช่น var s1 = “poolsawat.com” // จะได้ type string
  • การกำหนดค่าหลาย ๆ ค่า ก็สามารถ assings แบบนี้ var i, j int = 1, 2 ได้เช่นกัน
  • การประกาศค่าแบบสั้น ด้วย s1 := “poolsawat.com”
  • อื่น ๆ ตามโค๊ดตัวอย่างข้างล่าง
/////////////////////////////////
// Variables and Declarations
// Go Playground: https://play.golang.org/p/PKdAxUp8mNT
/////////////////////////////////
 
package main
 
import "fmt"
 
func main() {
 
    //** DECLARING VARIABLES **///
 
    // Syntax: var var_name type
    var s1 string
    s1 = "Learning Go!"
    fmt.Println(s1) // printing string s1
 
    //** TYPE INFERENCE **//
 
    // Go deduces automatically the type of the variable by looking at the initial value (bool, int, string etc)
 
    var k int = 6 // not necessary to say the type (int). It is inferred from the literal on the right side of =
    var i = 5     // type int
    var j = 5.6   // type float64
 
    // printing i, j and k
    fmt.Println("i:", i, "j:", j, "k:", k)
 
    // ii == jj  // -> error: cannot assign float to int (Go is a strong typed language)
 
    // declaring and initializing a new variable of type string (type inference)
    var s2 = "Go!"
    _ = s2 //in Go each variable must be used or there is a compile-time error
    // _ is the Blank Identifier and mutes the error of unused variables
    // _ can be only on the left hand side of the = operator
 
    // multiple assignments
    var ii, jj int
    ii, jj = 5, 8 // -> tuple assignment. It allows several variables to be assigned at once
 
    // swapping two variables using multiple assignments
    ii, jj = jj, ii
 
    fmt.Println(ii, jj)
 
    //** Short Declaration (works only in Block Scope!) **//
 
    // := (colon equals syntax) used only when declaring a new variable (or at least a new variable)
    // := tells go we are going to create a new variable and go figures out what type it will be
    s3 := "Learning golang!"
    _ = s3
 
    // can't use short declaration at Package Scope (outside main() or other function)
    // all statements at package scope must start with a Go keyword (package, var, import, func etc)
 
    // multiple short declaration
    car, cost := "Audi", 50000
    fmt.Println(car, cost)
 
    // redeclaration with short declaration syntax
    // at least one variable must be NEW on the left side of :=
    var deleted = false
    deleted, file := true, "a.txt"
    _, _ = deleted, file
 
    // expressions in short declarations are allowed
    sum := 5 + 2.5
    fmt.Println(sum)
 
    // multiple declaration is good for readability
    var (
        age       float64
        firstName string
        gender    bool
    )
    _, _, _ = age, firstName, gender
 
    // a concise way to declare multiple variables that have the same type
    var a, b int
    _, _ = a, b
 
}

Types and Zero Values

ชนิดของตัวแปร และค่า 0 (ศูนย์)

  • ตัวอย่าง type ของ GO เช่น string, int, float และอื่น ๆ เพิ่มเติม
  • หากไม่ initial value ให้ GO จะ set default value ให้ int // initialized with 0, float //initialized with 0.0,bool //initialized with false และ string //initialized with empty string
  • การตรวจสอบ type ของตัวแปร สามารถทำได้หลายวิธี 1 ในวิธีที่ง่าย คือ %T ตัวอย่าง fmt.Printf(“The type of name is: %T\n”, name)
/////////////////////////////////
// Types and Zero Values
// Go Playground: https://play.golang.org/p/zItROROXi64
/////////////////////////////////
 
package main
 
import "fmt"
 
func main() {
 
    // you must provide a type for each variable you declare or Go should be able to infer it
    var a int = 10
    var b = 15.5      // type inference (deduction)
    c := "Gopher"     // short declaration, type inference
    _, _, _ = a, b, c // Blank Identifier (_) to get rid of unused variable error
 
    // Go is a Statically and Strong Typed Programming Language
    // a = 3.14 -> error. A variable cannot change it's type
    // a = b    -> error. It's not allowed to assign a type to another type
 
    //** ZERO VALUES **//
 
    // An uninitialized variable or empty variable  will get the so called ZERO VALUE
    // The zero-value mechanism of Go ensures that a variable always holds a well defined value of its type
    var value int                         // initialized with 0
    var price float64                     // initialized with 0.0
    var name string                       // initialized with empty string -> ""
    var done bool                         // initialized with false
    fmt.Println(value, price, name, done) // -> 0 0.0 ""  false
}

Comments and Naming Convention

การ comment code และการตั้งชื่อตัวแปร และค่าอื่น ๆ
  • comment บรรทัดเดียว จะใช้ // my code un use
  • comments หลาย ๆ บรรทัด /* my code un use multi lines */
  • การตั้งชื่อไม่ควรยาวเกินความจำเป็น ต้องสื่อกับหน้าที่ของชนิดนั้น ๆ และต้องง่านต่อการดูในภายหลัง
  • ไม่ควรตั้งชื่อที่ใช้ _ (underscore) ในการตั้งชื่อ
/////////////////////////////////////////
// Comments and Naming Conventions in Go
// Go Playground: https://play.golang.org/p/pprI80SPMkS
/////////////////////////////////////////
 
package main
 
//** COMMENTS **//
 
// this is a single line comment
 
/*
 This is a block comment.
 a := 10
 fmt.Println(a)
*/
 
var name = "John Wick" // inline comment
 
//** NAMING CONVENTIONS IN GO **//
 
// Naming Conventions are important for code readability and maintainability.
 
// use short, concise names especially in shorter scopes
// common names for common types:
var s string   //string
var i int      //index
var num int    //number
var msg string //message
var v string   //value
var err error  //error value
var done bool  //bool, has been done?
 
// use mixedCase a.k.a camelCase instead of snake_case (variables and  functions)
var maxValue = 100  // recommended (camelCase)
var max_value = 100 // not recommended (snake_case)
 
// recommended
func writeToFile() {
}
 
// not recommended
func write_to_file() {
}
 
// write acronyms in all caps
var writeToDB = true // recommended
var writeToDb = true // not recommended
 
func main() {
 
    // use fewer letters, don’t be too verbose especially in smaller scopes
    var packetsReceived int // NOT OK, too verbose
    var n int               // OK
    _, _ = packetsReceived, n
 
    // an uppercase first letter has special significance to go (it will be exported in other packages)
}

Golang เริ่มต้น Golang Developer EP1

สวัสดีครับ พอมีเวลาว่าง ๆ เริ่มศึกษาภาษาใหม่ ๆ ก็อยากที่จะทำ series ของ Golang ไว้กับชุดบทความนี้

ติดตั้ง golang และ ทำความรู้จัก GOROOT, GOPATH

  • download golang version ล่าสุด https://golang.org/dl/
  • หลังจากติดตั้ง golang แล้ว ตรวจสอบเวอร์ชั่น ด้วย ‘go version
  • GOROOT กำหนด go compile path ด้วยคำสั่ง export GOROOT=/usr/local/go
  • GOPATH กำหนด go path ของ project ด้วย export GOPATH=$HOME/go
  • set PATH หลัก ด้วย export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

เริ่ม Print “Hello Golang”

  • สร้าง directory เพื่อเก็บ code ของ workshop นี้ไว้ที่ “$GOPATH/src/master_go_programming/hello_world
  • สร้างไฟล์ “main.go” ใส่โค๊ดนี้ในไฟล์ main.go
package main

import "fmt"

func main() {

    fmt.Println("Hello Golang")

}
  • สั่ง go run เพื่อ compile main.go ด้วย ‘go run main.go’

Editor VScode และ extensions

รีวิวเกมส์ NFT MOO Monster เปิดเล่นวันแรกเป็นอย่างไร ติดตั้ง เชื่อมต่อกระเป๋า อธิบายระบบเกมส์เบื้องต้น

*** การลงทุนมีความเสี่ยง ผู้ลงทุนควรศึกษาข้อมูลก่อนการตัดสินใจลงทุน ***

ทำความรู็จัก Moo Monster มันคือเกมส์อะไร

Moo Monster เป็น NFT Game ฝีมือคนไทยที่ได้เปิดรอบ IDO ไปเมื่อช่วงตุลาคม ปีที่แล้ว สำหรับผู้เล่นมือใหม่สามารถเข้าเล่นได้โดยไม่ต้องลงทุน ตัวเกมส์จะเป็นแนวโจมตีโดยไม่ต้องรอรอบเล่น (เมื่อเปิดสู้ ตัวละครจะโจมตีใส่กันไม่ยั้งตามสกิล และสถานะของแต่ละตัวละคร) สามารถอ่าน white paper ของเกมส์เพื่มเติมได้ ที่นี่ White Paper

การเข้าเล่น และผลตอบแทนของเกมส์

เกมส์จะเป็นลักษณะแบบ Play to Earn โดยผู้เล่นจะต้องจัดทีมตัวละคร และสู้กับ Monster ตามด่านต่าง ๆ โดยแต่ละด่านก็จะเพิ่มความยาก และเลเวลของ Monster ก็จะสูงขึ้นไปด้วย เมื่อต่อสู้ทำลาย Monster ฝั่งตรงข้ามไปจนหมดแล้ว คุณจะถือว่าเป็นผู้ชนะ และจะได้ reward เป็น gMoo โดยการเล่นแต่ละรอบจะได้ สามารถสมัครเข้าเล่นได้จากลิ้งนี้ https://app.moo-monster.com/inventory

Reward 0.25 Moo

เริ่มเข้าเล่นเกมส์

ระบบเกมส์จะมีส่วนที่เป็นจัดการสิ่งของผู้เล่น ที่ใช้เป็นที่สำหรับเติม/ถอน Moo ของเรานั้นเอง โดยเริ่มแรกเราจะต้องทำการ Download Moo Game Mobile (Andoid/IOS) ติดตั้งบนอุปกรณ์ให้เรียบร้อย (Emulator ก็สามารถเล่นได้ ข้อดีจะสามารถสร้าง account ได้หลายไอดี) จากนั้น login เกมส์ให้เรียบร้อย

ถัดไปให้เข้าไปเชื่อมต่อเกมส์ (ลิ้งหน้าเกมส์ที่จะเชื่อมกับ Metamask) กับ wallet ของเราซะก่อน (การเชื่อม wallet metamask) จากนั้นระบบจะให้ผู้เล่นสร้าง account สำหรับลิ้งกับ wallet กับ Moo Application Game (Andoird/IOS) โดยจะให้ใช้ลิ้งกันด้วย G-mail , Facebook (ควรเลือก Account เดียวกับที่เลือก login บน Moo Mobile App)

หรือใครอยากจะอ่านรายละเอียดเพิ่มเติมสามารถเข้าไปอ่านและทำตามขั้นตอนจาก medium.com official ของทีมงาน Moo Monster ได้เลย (ลิ้งขั้นตอนวิธีการเข้าเล่น Moo Monster)

การทำขั้นตอนดังกล่าวนี้เป็นการลิ้ง account ของระบบเกมส์ เมื่อลิ้งกันเรียบร้อยแล้วให้เข้าไปที่หน้า Moo App บนมือถือ (Emulator) ให้ทำการ Refresh Data ที่เมนูตั้งค่าเพื่อดึงตัวละคร Moo ของเราให้เกมส์สามารถมองเห็นได้ โดยไอคอนตั้งค่าจะอยู่ บนขวาของตัวเกมส์

ถ้าทำขั้นตอนเหล่านี้เรียบร้อยระบบเกมส์ก็จะสามารถดึง NFT Moo ของเรามาในเกมส์ได้แล้ว

รายละเอียดเกมส์บางส่วน

เมื่อเข้ามาหน้าแรกของเกมส์ระบบจะแสดงตัวละครหลัก (คน) กับตัวละครผู้ช่วย (Moo) ระบบเกมส์มีระบบการอัพเกรดสเตตัสตัวละครคน ,หมู ของเราได้ โดยหนัก ๆ จะต้องใช้ไอเทมผลไม้ และ Moo บางส่วนเพื่ออัพเกรด

การจัดทีมเข้าต่อสู่ ระบบเกมส์จะให้เตรียมทีมได้ 5 ตัวละคร จากนั้นจะให้เข้าต่อสู้โดยมีด่านต่าง ๆ ตามระดับความสามารถของตัวละคร

ผลตอบแทน การ Claim Reward

เมื่อผู้เล่นได้ทำการเล่นและได้ reward แล้วจพนวนนึงระบบเกมส์จะให้ผู้เล่นสามารถเข้าไป claim ได้ใน moo wallet

ช่วงเปิดตัวเกมส์วันนี้จะมีเงื่อนไขการถอนเหรียญ ดังนี้
Min-Max: 50-500 Moo
Fee: 50%

คะแนนความสนุก และความน่าลงทุน

คะแนนความสนุก 7/10 ระบบเกมส์ทำมาสวย

ความน่าลงทุน 7/10 ให้เหตุผล ทีมพัฒนาน่าจะเห็นตัวอย่าง สำหรับเรื่องระบบเหรียญที่เฟ้อจากเกมส์อื่น ๆ แต่เกมส์เพิ่งจะเปิดไว้ใจไม่ได้เหมือนกัน

รีวิวเกมส์ NFT CryptoCar ดอยสูงมาก ข้อมูลต่าง ๆ เกี่ยวกับ CryptoCars

*** การลงทุนมีความเสี่ยง ผู้ลงทุนควรศึกษาข้อมูลก่อนการตัดสินใจลงทุน ***

ทำความรู้จัก Crypto Car มันคืออะไร

1 ในเกมส์ของค่าย Crypto City เราอาจจะพอเคยได้ยิน หรือรู้จัก CryptoCars (แข่งรถ), CryptoPlanes (แข่งเครื่องบิน), CryptoGuards (แข่งยิงปืน)

CryptoCars เกมส์แนวแข่งรถ ระบบเกมส์จะให้เราทำการเปิดกล่องเพื่อลุ้น NFT รถแข่งที่มีอยู่ด้วยกัน 5 เลเวล Classic, Common, Super Car, Rare, Legendary โดยแต่ละเลเวลจะมีจำนวนรอบการแข่งต่อวันไม่เท่ากัน โดยปกติ Classic, Common เป็นให้ 4 รอบแข่งต่อวัน เป็นต้น และเลเวลที่สูงขึ้นก็จะได้รอบเพิ่มเติมไปด้วย

โดยเกมส์เริ่มเปิดให้เล่นเมื่อช่วงต้นกันยายน ราคาเปิดตัวเกมส์ 17 บาท ปัจจุบัน ราคาเกือบจะเหลือ 0 โดยยังอยู่ที่ 4 บาท (ดอยสูงมาก) เกมส์จะให้ reward ตอบแทนเป็น ccar

การเล่น และผลตอบแทนของเกมส์

เกมส์จะมีรูปแบบการเล่นเป็นแนวจำลองการแข่งรถโดยให้ผลตอบแทนตามลำดับผลการแข่งขัน 1st (10 ccar + exp), 2nd (7.5 ccar + exp ) , 3rd (5 ccar + exp ) , 4th (2.5 ccar + exp ) , 5th (exp)

จากภาพตัวอย่างรภ เลเวล common จะมีรอบวิ่งได้ 4 รอบ เมื่อแข่งครบ 4 รอบจะแข่งรอบของวันถัดไปได้ต้องรอเวลาอีก 24 ชม. ถึงจะสามารถเข้ามาเติมน้ำมันและเริ่มกดแข่งได่อีกครั้ง

เริ่มเล่นเกม แนะนำเมนู

ทำการต่อกระเป๋า Metamask เข้ากับหน้าเกมส์ Crypto Car หน้าแรกของเกมส์จะมี Crypto Lotery ให้เสี่ยงดวงกัน สุ่มเลขละ 20 CCAR โดยจะได้รางวัล 1 20%, 2 8% ,3 4%, 4 1% ของยอดการรวมทายผล

ถัดมา ต้องทำการเติม CCAR เข้าเกมส์ก่อน ผ่านเมนู Deposit เติมเหรียญ, Withdraw ถอนเหรียญ

เมื่อเติม CCAR เข้าเกมส์แล้วจะมีวิธีซื้อรถด้วยกัน 2 แบบ

1. เปิดกล่องเพื่อลุ้นรถที่มีเลเวล Common , Classic, Super Car, Rare, Legendary

2. ซื้อจากตลาดรถ Maketplace มีตั้งแต่ราคา 550 CCAR เป็นต้นไปตามราคา


ค่าใช้จ่ายเมื่อซื้อรถทั้ง 2 แบบ

  1. การเปิดกล่องสุ่ม จะมีเงื่อนไขการเปิดอย่างน้อยต้องมีรถในความครอบครองไม่เกิน 5 คันถึงจะสามารถเปิดกล่องสุ่มได้ โดยแต่ละกล่องจะใช้ 550 CCAR ในการเปิด, จะมีค่าจดทะเบียนรถอีก 10 CCAR และจะต้องใช้อีกวันละ 2 CCAR ในการเติมน้ำมันเชื้อเพลิง
  2. จะจ่ายแต่ราคารถ XXX CCAR เท่านั้น เพราะรถผ่านการจดทะเบียนมาแล้วจึงไม่จำเป็นต้องเสียค่าจดทะเบียน
    * โดยทั้ง 2 แบบจะต้องเสียค่าเติมน้ำมัน 2 CCAR ในแต่ละวันถึงจะสามารถแข่งต่ออีกได้

การ Claim Reward

เงื่อนไขการ Lock เหรียญจะ Lock ไว้ 5 วันโดยมีการหัก Fee 40%, 30%, 20% 10%

คะแนนความสนุก และความน่าลงทุน

คะแนนความสนุก 5/10

ความน่าลงทุน 6/10 ให้เหตุผลว่าใช้ทุนไม่เยอะมาก

รีวิวเกมส์ NFT Bombcrypto เกือบ 2 เดือนเป็นอย่างไรบ้าง พร้อมเรื่องที่ต้องรู้เกี่ยวกับ bombcrypto

*** การลงทุนมีความเสี่ยง ผู้ลงทุนควรศึกษาข้อมูลก่อนการตัดสินใจลงทุน ***

เป็นอีกเกมส์ที่จะดูมีอนาคตกว่าเกมส์อื่น ๆ ของ gamefi ในวงการ NFT ที่ผมรู้จัก สำหรับเกมส์ bombcrypto ผมได้ลองลงทุนเล่นมาได้เกือบ 2 เดือน วันนี้จะมารีวิวว่าในช่วงเวลาเกือบ 2 เดือนได้ประสบการณ์อะไรจากเกมส์นี้บ้าง

ทำความรู้จัก Bombcrypto กันก่อน

Bombcrypto เปิดเกมส์มาในช่วงต้นเดือนตุลาคม 2564 เป็นเกมส์แนว play to earn ก็คือ ต้องเล่นถึงจะได้รางวัลตอบแทน

https://bombcrypto.io/

โดยตัวเกมส์เมื่อได้เห็นในครั้งแรกจะทำให้เรานึกถึงตัวเกมส์ Bomber Man เกมส์ฮิตทุกยุคทุกสมัย แต่มีการปรับรูปแบบของเกมส์ให้น่าเล่น และให้เข้ากับการให้ reward สำหรับเกมส์ NFT ในแบบของ bombcrypto

การเล่น และผลตอบแทนของเกมส์

ทุก ๆ เกมส์ของ NFT Game จะมีสิ่งหนึ่งที่คล้าย ๆ กันคือ ทุกเกมส์จะมีเหรียญ ( crypto coin ) ของแต่ละเกมส์ สำหรับ bomb ก็มีเหรียญของเกมส์เหมือนกัน ที่ชื่อว่า Bombcrypto (bcoin) ซึ่งช่วงเปิดตัวของเกมส์ราคาเหรียญอยู่ที่ 58 บาท และเคยทำราคาไปได้ถึง 281 บาทเมื่อช่วงปลายเดือนพศจิกายน ปัจจุบันของเหรียญอยู่ที่ 112 บาท (ราคา ณ วันที่ลงบทควาท) โดยการที่จะ reward รางวัล (bcoin) ของเกมส์นั้นคือการจะต้องวางระเบิดทำลายกล่องในแต่ละด่านโดยแต่ละกล่องจะมี bcoin ซ่อนอยู่ นั้นคือรางวัลทีจะได้รับ

เริ่มเล่นเกมส์ แนะนำเมนู

เริ่มต้นผู้เล่นทุกคนต้องมีกระเป๋าเก็บเหรียญ การติดตั้ง Metamask และสร้างกระเป๋า ให้เรียบร้อย จากนั้นเชื่อมต่อกระเป๋าที่ได้ทำการสร้างกับ https://app.bombcrypto.io/ ให้เรียบร้อย

เมื่อเชื่อมกับกระเป๋าของเราได้เรียบร้อย เกมส์ก็จะพาเรามาที่หน้าแรกของเกมส์โดยมีรายละเอียดแต่ละเมนูตามนี้

Taeasure Hunt : โหมดเล่นแบบปกติ ใช้ตัวละครสูงสุด 15 ตัว ช่วยกันทำลายกล่อง เพื่อผ่านด่าน

Chest: สิ่งของ ตัวละคร

House : บ้านพักตัวละคร

Heros : ตัวละคร

ซื้อตัวละคร และการเล่นเกมส์


Heros : 1 ตัวละคร เราจะใช้ 10 bcoin ในการซื้อตัวละคร (ซื้อแบบสุ่ม เพื่อโอกาสที่จะได้ตัวละคร) โดยระดับของตัวละครตามดวง
common *
rare **
super rare ***
legend ****
super regend *****
* คือความสามารถของตัวละคร
Taeasure Hunt : โหมดการเล่นที่จำเป็นต้องเลือกตัวละครเพื่อเป็นทีมช่วยทำลายกล่อง และจะได้ reward bcon

Adventure : เปิดให้เข้าเล่นได้ 18 มกรา 64

การ Claim Reward

เราจะสามารถ claim bcoin ได้ก็ต่อเมื่อมีเหรียญในหีบครบ 40 เหรียญแล้ว โดยจำเป็นอย่างมาก ที่ใน wallet (metamask ต้องมีเหรียญค้างในกระเป๋า 1 bcoin) ไม่เช่นนั้นจะไม่สามารถ claim ได้

คะแนนความสนุก และความน่าลงทุน

ผมให้ความสนุกจากการลุ้นตัวละคร 8/10
ความน่าลงทุน 8/10

ข้อมูลอื่น ๆ ที่เกี่ยวข้อง

BombCrypto Thailand (กลุ่มพูดคุยหลัก) กลุ่ม facebook

ตรวจสอบ ROI ของเกมส์ Bombcrypto

ตรวจสอบการ claim bcoin ,claim hero

ตารางข้อมูลการอัพเกรดตัวละคร

How would you like to pay ?

Vocabulary Put these service in the right group

free shippingexpress mail servicebuy one get one free
QR code paymentfree giftscash
registered mailgift cardbank transfers
pick up from the store30% discount voucherbuy one get one half off
PromotionsPayment serviceDelivery service
buy one get one free
free gifts
30% discount voucher
buy one get half off
QR code payment
Cash
Bank transfers
gift card
free shipping
express mail service
registered mail
pick up from the store

Language focus: Asking about payment and delivery service

PromotionsMaking a payment
Do you have any sale promotions at the moment?
– We have …
– We offer …
– Alright, I’ll take it.
– Let me look around first.
How would you like to pay?
– I will pay by _____________, please.
-__________________, please
-______________________ is fine by me.
— cash
–debit/credit card
–QR code payment
–bank transfer
Delivery
Can you send it by ___________________ ?
– an express mail service
– a registered mail.
– ______[name] courier service.
Do you have free shipping?
Will I get the product by / within __________ ?
Can I pick it / them up from the store instead ?

Sharing opinion

Vocabulary: Complete the questions with the given words. Then share and explain you answers

rush hour (ชั่วโมงเร่งรีบ)manage (จัดการ)dream destination (ปลายทางแห่งความฝัน)make up to someone (ขอคืนดีกับใครสักคน)
fortune telling (ทำนายอนาคต)see eye to eye (เห็นตรงกัน)volunteering activity (กิจกรรมอาสาสมัคร)deal with (จัดการกับเรื่องอะไรสักอย่าง)
  1. Where is your dream desination? Why do you want to go there?
  2. Do you belive in fortune telling? What questions would you ask to know about your future?
  3. What’s the best way to make up to someone after a fight?
  4. What’s the best way to get around during rush hour in big cities?
  5. What is the best way to deal with unfriendly colleagues?
  6. What kind of volunteering activity do you prefer? Building houses or feeding animals?
  7. What’s the best way to manage time better?
  8. In your opinion is it important to see eye to eye with your boss?

Language Focus I useful Exprssion for Expression Opinions

Aksing for and OpinionExpression an Opinion
What do you think about [noun / v.ing ]?In my opinion
Do you agree that [subject + verb] / v.ing?In my view
Do you thing that [subject + verb] / v.ing?I think / belive / guess that

Practice noun clause start with “that”

  1. Do / Important / that / you / agree / is / reading / ?
    Do you agree that reading is important ?
  2. think / you / students / that / shouldn’t / questions / ask / Do / ?
    Do you think that students shouldn’t ask questions ?
  3. in / view / children / listen / should / to my / parents.
    In my view children should listen to my parents.