Advanced Guides
การบูรณาการการทดสอบใน Parse Cloud Code บน Back4App
28 นาที
วิธีการรวมการทดสอบเข้ากับฟังก์ชัน parse cloud code ของคุณ บทนำ นี่คือคู่มือที่เขียนโดย https //github com/considine , นักเขียนรับเชิญของเราและนักพัฒนาหลักที่ https //koptional com/ บทเรียนนี้ครอบคลุมวิธีการตั้งค่าการทดสอบอัตโนมัติสำหรับ back4app cloud code ของคุณ เราจะพูดคุยสั้น ๆ เกี่ยวกับการย้ายโค้ด parse ของลูกค้าบางส่วนไปยังคลาวด์ และจากนั้นเกี่ยวกับวิธีการรวมโครงการของคุณเข้ากับระบบนิเวศการทดสอบ คุณยังสามารถตรวจสอบ โครงการตัวอย่าง https //github com/back4app/template cloud code unit test โดยตรงเพื่อดูเวอร์ชันที่ใช้งานได้ เป้าหมาย เราหวังว่าจะรวมแง่มุมที่แข็งแกร่งและสามารถขยายได้ของการทดสอบอัตโนมัติเข้ากับสภาพแวดล้อม parse ที่เป็นมิตรกับนักพัฒนา โดยการใช้ cloud code ซึ่งอาจเป็นฟีเจอร์ที่ไม่ได้รับการชื่นชมมากนักของ parse นักพัฒนาสามารถทำการปรับปรุงโค้ดของตนได้อย่างรวดเร็วและมั่นใจได้ว่าโปรแกรมจะทำงานตามที่คาดหวัง การพัฒนาที่ขับเคลื่อนด้วยการทดสอบ https //en wikipedia org/wiki/test driven development เป็นสาขาที่กว้างใหญ่; แทนที่จะพูดในเชิงปรัชญาเกี่ยวกับการทดสอบ เราจะทำการดำเนินการและพูดคุยเกี่ยวกับกลยุทธ์บางอย่าง (เช่น การทำ stub) ข้อกำหนดเบื้องต้น ในการทำตามบทเรียนนี้ คุณต้องมี แอปที่ back4app ทำตาม บทเรียนการสร้างแอปใหม่ เพื่อเรียนรู้วิธีการสร้างแอปที่ back4app back4app command line ที่กำหนดค่าไว้กับโปรเจกต์ ทำตาม บทเรียนการตั้งค่า cloud code เพื่อเรียนรู้วิธีการตั้งค่า cloud code สำหรับโปรเจกต์ ติดตั้ง npm บนบรรทัดคำสั่งของคุณ หมายเหตุ ไลบรารีนี้จะใช้ https //www promisejs org/ , ซึ่งไม่ควรจะซับซ้อนเกินไป มาสร้างแบ็กเอนด์โซเชียลมีเดียพื้นฐานกันเถอะ โอเค! ลองนึกภาพแอปโซเชียลมีเดียที่มีโมเดลโปรไฟล์เพื่อใช้ร่วมกับโมเดลผู้ใช้ สำหรับแอปบางตัว คุณอาจใส่ข้อมูลโปรไฟล์ในโมเดลผู้ใช้ แม้ว่าจะไม่ค่อยมีประสิทธิภาพในหลายกรณี; คุณมักจะต้องแยกความกังวลเกี่ยวกับการอนุญาต/การตรวจสอบตัวตนกับเนื้อหาของผู้ใช้ และดังนั้นจึงต้องรักษาโมเดลที่แตกต่างกันสองโมเดล ในบทแนะนำนี้ เราจะดำเนินการฟีเจอร์ที่จัดการการสร้างผู้ใช้และโปรไฟล์ในลักษณะนี้ โดยมีแรงกดดันน้อยที่สุดต่อไคลเอนต์ มาลงมือทำกันเถอะ! 1\ การกำหนดฟังก์ชันของเรา นี่ถือว่าคุณมีโปรเจกต์ back4app ที่สร้างขึ้นแล้ว และเครื่องมือบรรทัดคำสั่งติดตั้งแล้ว (ดูข้อกำหนดเบื้องต้น) สำหรับตัวอย่างของโค้ดฝั่งหน้า คู่มือนี้จะอ้างถึงไวยากรณ์ parse javascript sdk เพื่อความเรียบง่าย เมื่อมีคนลงทะเบียนสำหรับแอปพลิเคชันนี้ โปรไฟล์ควรจะถูกสร้างขึ้นและเชื่อมโยงกับวัตถุผู้ใช้ ฟังก์ชันการลงทะเบียน ในหลายแอปพลิเคชัน parse คุณจะสร้างผู้ใช้ด้วยไวยากรณ์ต่อไปนี้ 1 var user = new parse user(); 2 user set("username", "my name"); 3 user set("password", "my pass"); 4 user set("email", "email\@example com"); ในกรณีของเรา เราต้องการเริ่มต้นโปรไฟล์ด้วย และชี้ไปที่วัตถุผู้ใช้ parse server 3 x 1 try { 2 await user signup(null, {usemasterkey true}); 3 let profile = parse object extend("profile"); 4 let profile = new profile({ 5 firstname params firstname, 6 lastname params lastname, 7 user user 8 }) 9 return profile save(null, {usemasterkey true}); 10 } catch (err){ 11 return (err message); 12 } parse server 2 x 1 user signup(null, { 2 success function (newuser) { 3 var profile = parse object extend("profile"); 4 var profile = new profile(); 5 profile set("firstname", "john"); 6 profile set("lastname", "smith"); 7 profile set("user", newuser); 8 profile save(); 9 }, 10 error function (err) { 11 // handle error 12 } 13 }) คุณสามารถทำให้ไวยากรณ์นั้นสั้นลงให้เป็นอะไรประมาณนี้ parse server 3 x 1 let user = new parse user({ 2 username params username, 3 password params password, 4 email params email 5 }); 6 7 try { 8 await user signup(null, {usemasterkey true}); 9 let profile = parse object extend("profile"); 10 let profile = new profile({ 11 firstname params firstname, 12 lastname params lastname, 13 user user 14 }) 15 return profile save(null, {usemasterkey true}); 16 } catch (err){ 17 return (err message); 18 } parse server 2 x 1 var user = new parse user({ 2 username params username, 3 password params password, 4 email params email 5 }); 6 user signup(null) 7 then((newuser) => { 8 var profile = parse object extend("profile"); 9 var profile = new profile({ 10 "firstname" "john", 11 "lastname" "smith", 12 "user" newuser 13 }); 14 return profile save(); 15 }) น่าเสียดายที่สิ่งนี้ยังเกี่ยวข้องกับการทำคำขอแยกสองคำขอไปยัง parse server ซึ่งไม่เป็นประโยชน์สำหรับ frontend; ควรหลีกเลี่ยงการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์หลายขั้นตอนเมื่อเป็นไปได้ นอกจากนี้เกี่ยวกับความปลอดภัย โค้ดข้างต้นกำลังมอบกระบวนการสร้างให้กับไคลเอนต์ซึ่งไม่ใช่เรื่องที่ฉลาดเลย เราต้องการป้องกันไม่ให้ความสมบูรณ์ของข้อมูลของเราขึ้นอยู่กับการที่ไคลเอนต์ทำขั้นตอนทั้งหมดของกระบวนการอย่างถูกต้อง พวกเขาอาจส่งคำขอที่กำหนดเองซึ่งสร้างผู้ใช้โดยไม่มีโปรไฟล์ ทำให้ข้อมูลที่คงอยู่ของแอปเสียหาย ทำไมไม่ทำทั้งหมดนี้ในขั้นตอนเดียวโดยใช้โค้ดคลาวด์? มันสามารถป้องกันการบวมของโค้ด frontend และทำให้แน่ใจว่าไคลเอนต์ไม่ได้ทำงานที่ไม่จำเป็น/ไม่ปลอดภัย! นี่คือสิ่งที่เราต้องการทำแทนจากไคลเอนต์สำหรับการลงทะเบียน 1 parse cloud run('signupuser', 2 { 3 username 'myname', 4 password "mypass", 5 email "email\@example com", 6 firstname "john", 7 lastname "smith" } 8 ) then(function(newuser) { 9 10 }); parse ยังได้กำหนด http //docs parseplatform org/cloudcode/guide/#beforesave triggers triggers ซึ่งอนุญาตให้สร้างโปรไฟล์เมื่อผู้ใช้ลงทะเบียน อย่างไรก็ตามโดยการใช้ฟังก์ชันเราสามารถส่งผ่านแอตทริบิวต์ firstname และ lastname ที่โปรไฟล์จะใช้ได้อย่างมีสัญชาตญาณ ฟังก์ชันลงทะเบียนโค้ดคลาวด์ เริ่มกันเลย! ย้ายไปยังไดเรกทอรีโปรเจกต์ของคุณที่ซิงค์กับ back4app (ดูข้อกำหนดเบื้องต้นหากคุณไม่รู้ว่านี่หมายถึงอะไร) เราจะสมมติโครงสร้างดังต่อไปนี้ ในกรณีของเรา เมื่อเริ่มต้น เราเลือก ‘cloud’ เป็นชื่อไดเรกทอรีของเรา ไดเรกทอรีของคุณสามารถตั้งชื่ออะไรก็ได้ที่คุณต้องการ parse server 3 x main js 1 parse cloud define("signuserup", async(request) => { 2 // make sure the necessary parameters are passed first 3 let params = request params; 4 if (!params username || !params email || !params password || !params firstname || !params lastname) 5 throw new error("missing parameters need username, email, password, firstname, & lastname"); 6 7 // execute the signup flow 8 let user = new parse user({ 9 username params username, 10 password params password, 11 email params email 12 }); 13 14 try { 15 await user signup(null, {usemasterkey true}); 16 let profile = parse object extend("profile"); 17 let profile = new profile({ 18 firstname params firstname, 19 lastname params lastname, 20 user user 21 }) 22 return profile save(null, {usemasterkey true}); 23 } catch (err){ 24 return (err message); 25 } 26 }); parse server 2 x main js 1 parse cloud define("signuserup", function(request, response) { 2 // make sure the necessary parameters are passed first 3 var params = request params; 4 if (!params username || !params email || !params password || !params firstname || !params lastname) 5 return response error("missing parameters need username, email, password, firstname, & lastname"); 6 7 // execute the signup flow 8 var user = new parse user({ 9 username params username, 10 password params password, 11 email params email 12 }); 13 user signup(null, {usemasterkey true}) 14 then((newuser) => { 15 var profile = parse object extend("profile"); 16 var profile = new profile({ 17 firstname params firstname, 18 lastname params lastname, 19 user newuser 20 }) 21 return profile save(null, {usemasterkey true}); 22 }) 23 then((prof) => response success(prof)) 24 catch((e) => { 25 response error(e message); 26 }) 27 }); คุณอาจสังเกตเห็นตัวเลือก ‘usemasterkey’ ที่ถูกส่งไป; สิ่งนี้ช่วยให้โค้ดคลาวด์สามารถเหนือกว่าบทบาทหรือ acl ที่อาจมีอยู่ได้ เนื่องจากไคลเอนต์ไม่แตะต้องโค้ดนี้ จึงไม่มีความเสี่ยงที่พวกเขาจะเข้ายึดเซิร์ฟเวอร์ของเรา อย่างไรก็ตาม โปรดระมัดระวังกับธงนี้! ในกรณีที่ไม่ชัดเจนว่าทำไมสิ่งนี้อาจดีกว่าการวางฟังก์ชันนี้ในโค้ดของไคลเอนต์ นี่คือข้อดีบางประการ ส่งการคำนวณไปยังเซิร์ฟเวอร์แทนที่จะเป็นอุปกรณ์ กำหนดฟังก์ชันการทำงานของกระบวนการอย่างชัดเจน สร้างฟังก์ชันที่ปลอดภัยจากความล้มเหลวได้ง่ายขึ้น ให้ลูกค้ามีอินเทอร์เฟซที่เข้าใจได้ง่าย สิ่งนี้ป้องกันความเป็นไปได้ที่ลูกค้าจะ ‘ทำกระบวนการครึ่งหนึ่ง’ 2\ การปรับโครงสร้างไดเรกทอรีของเรา ยอดเยี่ยม เราได้สร้างฟังก์ชันคลาวด์สองฟังก์ชันแล้ว เราสามารถทดสอบฟังก์ชันเหล่านี้ได้โดยการรันและตรวจสอบ parse dashboard แต่สิ่งนั้นไม่สามารถขยายได้หรือมีประสิทธิภาพ เราจึงต้องการสร้างการทดสอบอัตโนมัติสำหรับวิธีการที่สามารถรันได้อย่างต่อเนื่อง ดังนั้นเราจะแยกโค้ดของเราออกเล็กน้อย เราจะย้ายฟังก์ชันที่เราสร้างใน main js ไปยังไฟล์ใหม่ที่เรียกว่า cloud functions js (ในไดเรกทอรีเดียวกัน) จากนั้นเราจะ นำเข้า ฟังก์ชันเหล่านี้ไปยัง main และผูกเข้ากับการกำหนดฟังก์ชันคลาวด์ แนวคิดคือการ แยก ฟังก์ชันออกจากอินเทอร์เฟซคลาวด์เพื่อที่เราจะได้ทดสอบโดยไม่ต้องส่งคำขอ http อย่างไม่มีประสิทธิภาพ สิ่งนี้จะมีความหมายมากเมื่อเราสร้างชุดทดสอบ สร้างไฟล์ฟังก์ชัน คุณอาจทราบว่าคุณสามารถใช้ ‘require’ ใน node js เพื่อดึงฟังก์ชัน วัตถุ และตัวแปรจากไฟล์อื่นๆ ดังนั้นเราจะกำหนดฟังก์ชันที่สอดคล้องกับฟังก์ชันคลาวด์ parse ที่เราสร้างในขั้นตอนที่ 1 จุดที่อาจทำให้สับสนคือฟังก์ชันที่เรากำลังนิยามจะเป็น ฟังก์ชันที่ส่งคืน , ซึ่งสามารถเชื่อมต่อกับการกำหนดคลาวด์ parse ได้ มันอาจดูแปลกที่จะใช้ฟังก์ชันเพื่อส่งคืนฟังก์ชัน แต่สิ่งนี้จะทำให้เรามีอำนาจในการเปลี่ยนเซิร์ฟเวอร์ parse ในภายหลังเมื่อเรากำลังเขียนการทดสอบของเรา คุณอาจสังเกตเห็นว่าคุณสามารถใช้วัตถุ parse ในโค้ดคลาวด์ของคุณได้ โดยไม่ต้องกำหนดหรือเรียกใช้งานมัน นี่เป็นผลมาจากเซิร์ฟเวอร์ที่รันโค้ดนี้เพิ่ม parse โดยอัตโนมัติ อย่างไรก็ตาม หากเราต้องการรันทดสอบฟังก์ชันในเครื่อง เราจะไม่ได้รับอนุญาตให้ใช้ตัวอย่าง ในความเป็นจริง เราต้องการจัดเตรียมตัวอย่างของเราเองที่สอดคล้องกับเซิร์ฟเวอร์ parse สำหรับการทดสอบ ซึ่งไม่มีอันตรายจากการสร้างหรือการลบข้อมูล ดังนั้นแต่ละฟังก์ชันจะรับ ‘parse’ เป็นพารามิเตอร์ และส่งคืนฟังก์ชันคลาวด์ parse server 3 x cloud functions js 1 // cloud functions js 2 module exports signupuser = function(parse) { 3 return async(request) => { 4 // copied from main js 5 // make sure the necessary parameters are passed first 6 let params = request params; 7 if (!params username || !params email || !params password || !params firstname || !params lastname) 8 throw new error("missing parameters need username, email, password, firstname, & lastname"); 9 10 // execute the signup flow 11 let user = new parse user({ 12 username params username, 13 password params password, 14 email params email 15 }); 16 17 try { 18 await user signup(null, {usemasterkey true}); 19 let profile = parse object extend("profile"); 20 let profile = new profile({ 21 firstname params firstname, 22 lastname params lastname, 23 user user 24 }) 25 return profile save(null, {usemasterkey true}); 26 } catch (err){ 27 return (err message); 28 } 29 } 30 } parse server 2 x cloud functions js 1 // cloud functions js 2 module exports signupuser = function(parse) { 3 return function (request, response) { 4 // copied from main js 5 // make sure the necessary parameters are passed first 6 var params = request params; 7 if (!params username || !params email || !params password || !params firstname || !params lastname) 8 return response error("missing parameters need username, email, password, firstname, & lastname"); 9 // execute the signup flow 10 var user = new parse user({ 11 username params username, 12 password params password, 13 email params email 14 }); 15 user signup(null, {usemasterkey true}) 16 then((newuser) => { 17 var profile = parse object extend("profile"); 18 var profile = new profile({ 19 firstname params firstname, 20 lastname params lastname, 21 user newuser 22 }) 23 return profile save(null, {usemasterkey true}); 24 }) 25 then((prof) => response success(prof)) 26 catch((e) => { 27 response error(e message); 28 }) 29 } 30 } ใน main js ให้ลบทุกอย่างจากก่อนหน้านี้ นำเข้าฟังก์ชันคลาวด์ และผูกฟังก์ชันกับการกำหนดฟังก์ชันคลาวด์แบบนี้ 1 // main js 2 var cloudfunctions = require(" /cloud functions"); 3 // note that we are injecting the parse instance, which is automatically supplied in the 4 // context of parse cloud code, but not on local tests 5 parse cloud define("signuserup", cloudfunctions signupuser(parse)); ยอดเยี่ยม! เราไม่ได้เปลี่ยนแปลงฟังก์ชันการทำงานเลยตั้งแต่ขั้นตอนที่ 1 แต่เราได้แยกฟังก์ชันออกจากโค้ดคลาวด์แล้ว ในขั้นตอนถัดไปเราจะสร้างการทดสอบหน่วย! 3\ สร้างชุดทดสอบ สำหรับชุดทดสอบของเรา เราจะใช้ jasmine https //jasmine github io/ , เฟรมเวิร์กการทดสอบที่ได้รับความนิยม อย่างไรก็ตาม โค้ดของเราจนถึงตอนนี้ไม่มีความเกี่ยวข้องกับการทดสอบของเราเลย ดังนั้นคุณสามารถใช้เฟรมเวิร์กหรือแพลตฟอร์มใดก็ได้ที่คุณต้องการ มาติดตั้ง jasmine และ jasmine node (การรวมกันของ jasmine และสภาพแวดล้อม node js ของเรา) ตอนนี้เรามาติดตั้งไลบรารีสองตัวที่ชุดทดสอบของเราจะใช้ มันจะใช้ parse sdk เพื่อเชื่อมต่อกับ parse server ปลอม และไลบรารี events สำหรับการ stub ออกวัตถุคำขอ ตอนนี้ โดยใช้เครื่องมือ jasmine มาตั้งค่าไดเรกทอรีทดสอบของเรากันเถอะ หากคุณต้องการ คุณสามารถติดตั้ง jasmine ทั่วไปด้วย $ npm install g jasmine $ npm install g jasmine , จากนั้นคุณสามารถตั้งค่าด้วย $ jasmine init $ jasmine init คู่มือนี้จะสมมติว่าคุณไม่ได้ติดตั้ง jasmine ทั่วไป แม้ว่าจะมีการแนะนำ หากคุณทำ คุณสามารถแทนที่ทุกกรณีของ ‘/node modules/jasmine/bin/jasmine js’ ด้วย ‘jasmine’ ได้ง่ายๆ นี่ควรสร้างไดเรกทอรีที่เรียกว่า spec ซึ่งรวมถึงโฟลเดอร์สนับสนุนที่มีข้อมูลการกำหนดค่าของ jasmine ตามค่าเริ่มต้น jasmine จะรู้ว่าจะมองหาไฟล์ที่ลงท้ายด้วยนามสกุล “ spec js” ดังนั้นเราจะตั้งชื่อการทดสอบของเราให้สอดคล้องกัน สร้างไฟล์สำหรับการทดสอบหน่วยแรกของเรา เพิ่มไดเรกทอรี utilities พร้อมไฟล์สองไฟล์ที่จะช่วยในการทดสอบของเรา สุดท้าย สร้างไฟล์ constants ในไดเรกทอรีเดียวกัน เครื่องมือสำหรับไฟล์นี้จะอธิบายภายหลัง นี่คือสิ่งที่ไดเรกทอรีของคุณควรมีลักษณะตอนนี้ 4\ การสลับไปยัง parse server ทดสอบ การทดสอบเกี่ยวกับ parse เนื่องจากวิธีการของเรามีส่วนเกี่ยวข้องกับ parse server เราจึงต้องการทดสอบการโต้ตอบนั้น มีสองวิธีในการทำเช่นนี้ a เราสามารถ “stub” ออกวัตถุ parse sdk โดยการกำหนดวัตถุที่ใช้การเชื่อมต่อเดียวกัน จากนั้นเพียงแค่ส่งวัตถุที่กำหนดนั้นเป็นพารามิเตอร์ไปยังเมธอดคลาวด์ของเรา ซึ่งอาจมีลักษณะดังนี้ 1 var parsestub = { 2 // make sure all used methods and properties are defined 3 user function () { 4 // constructor function 5 this set = function (key, val) { 6 // logic here to implement the parse object set 7 } 8 } 9 } 10 signupuser(parsestub); // returns cloud function that we can test b อีกวิธีหนึ่งคือการตั้งค่า parse server ที่แท้จริงซึ่งจะให้บริการเฉพาะข้อมูลทดสอบ ซึ่งจะเกี่ยวข้องกับเลเยอร์ http ที่ช้า ซึ่ง parse ใช้ แต่ก็จะช่วยให้เราทดสอบข้อมูลในฐานข้อมูลได้ ในการทดสอบของเราเราจำเป็นต้องนำเข้า parse sdk และกำหนดค่าให้กับเซิร์ฟเวอร์ทดสอบ สองสถานที่ที่สามารถ stub ได้เมื่อทดสอบโค้ดคลาวด์ a ) stub parse sdk ที่จะไม่ทำการร้องขอ http หรือ b ) สลับไปใช้การใช้งานฐานข้อมูลทดสอบ ไม่มีวิธีใดในสองวิธีนี้ที่เป็นคำตอบที่ “ถูกต้อง” ขึ้นอยู่กับสิ่งที่คุณพยายามจะทดสอบ การ stub อินเตอร์เฟซสำหรับ parse sdk (แม้เพียงแค่ส่วนที่เราใช้) เป็นงานที่ต้องใช้ความพยายามมาก นอกจากนี้เราจะทดสอบความคงอยู่ของข้อมูลหลังจากการบันทึกในตัวอย่างนี้ ดังนั้นเราจะใช้วิธีที่สอง มาทำกันเถอะ สร้าง parse server ทดสอบบน back4app ดึง application id และ master key และบันทึกลงในไฟล์ค่าคงที่ของเรา เริ่มต้น parse sdk ในไฟล์สเปคของเรา เพื่อให้การทดสอบของเราใช้เซิร์ฟเวอร์ทดสอบ คุณยินดีที่จะรัน parse server https //github com/parse community/parse server ในเครื่องของคุณสำหรับการทดสอบ เราจะสร้างแอปพลิเคชัน back4app อีกตัวในแดชบอร์ดของเรา หากคุณต้องการการทบทวนเกี่ยวกับวิธีการจัดเตรียมเซิร์ฟเวอร์ back4app อีกตัวหนึ่ง โปรดไปที่ บทช่วยสอนการสร้างแอปใหม่ https //www back4app com/docs/get started/new parse app ตั้งชื่อแอปพลิเคชันของคุณตามที่คุณต้องการ แต่การใช้ชื่อเช่น testbackend อาจเป็นความคิดที่ดี จากนั้นเพียงแค่ดึง application id และ master key จากแดชบอร์ด > การตั้งค่าแอป > ความปลอดภัย & คีย์ ตอนนี้บันทึกโทเค็นเหล่านี้ในไฟล์ค่าคงที่ของเราแบบนี้ 1 // /spec/constants js 2 // paste in your app id and master key where the strings are 3 module exports = { 4 application id "paste your application key here", 5 master key "paste your master key here" 6 } ห้ามใส่ application id และ master key จากแอปผลิตภัณฑ์ของคุณ!!! เราจะลบข้อมูล และการทำเช่นนี้จะเสี่ยงให้คุณสูญเสียข้อมูล 5\ เครื่องมือทดสอบ ฟังก์ชันคลาวด์จะถูกส่งเป็นพารามิเตอร์ในวัตถุคำขอและการตอบสนองของ express เซิร์ฟเวอร์จะสร้างพารามิเตอร์เหล่านี้โดยอัตโนมัติเมื่อทำงานบนคลาวด์ ดังนั้นสำหรับสภาพแวดล้อมการทดสอบของเรา เราต้องสร้างคู่ กรณีนี้ง่ายกว่า เมื่อเรียกฟังก์ชันคลาวด์ ข้อมูลจะถูกส่ง; ในกรณีของเรา ข้อมูลโปรไฟล์และผู้ใช้จะถูกส่ง ทุกอาร์กิวเมนต์ที่ให้สามารถเข้าถึงได้จากคุณสมบัติ request params ดังนั้นหากเราเรียกฟังก์ชันคลาวด์เช่น 1 // client code, calling parse function 2 parse cloud run('fakefunction', 3 { 4 data1 'i am data1', 5 data2 { 6 prop "nested property" 7 } 8 } 9 ); จากนั้นคุณสมบัติ request params จะมีข้อมูลที่ส่งมา 1 // server code, running the parse function 2 console log(request params); 3 // { 4 // data1 'i am data1', 5 // data2 { 6 // prop "nested property" 7 // } 8 // } ง่ายพอสมควร สำหรับการทดสอบของเรา เมื่อเรียกใช้ฟังก์ชันคลาวด์ของเรา อาร์กิวเมนต์แรกควรอยู่ในรูปแบบ 1 { 2 params { 3 username 'testuser', 4 firstname "john", 5 // the rest of the arguments 6 } 7 } ดังนั้นเราจึงไม่จำเป็นต้องสร้างวัตถุจำลองพิเศษในกรณีนี้ วัตถุการตอบสนองช่วยให้โค้ดคลาวด์ส่งการตอบสนอง http ไปยังไคลเอนต์ซึ่งแสดงถึงความสำเร็จหรือความล้มเหลว เราต้องการทราบว่าสิ่งนี้เรียกว่าอะไรเมื่อเรียกใช้ฟังก์ชันคลาวด์ ด้านล่างนี้คือ วัตถุจำลอง https //msdn microsoft com/en us/library/ff650441 aspx ที่จะช่วยให้การทดสอบของเราสามารถกำหนดได้ว่าการเรียกใช้งานประสบความสำเร็จหรือไม่ หากสิ่งนี้ทำให้สับสน ไม่ต้องกังวล แค่ใส่มันในไฟล์ /spec/utils/response stub js ของคุณ 1 // /spec/utils/response stub js 2 const eventemitter = require('events'); 3 / 4 wrapper around response stub simplifies testing cloud functions that 5 employ a response parameter 6 / 7 function responsestub () { 8 this responselistener = new eventemitter(); 9 this responsestub = {}; 10 / 11 success method that cloud functions expect 12 / 13 this responsestub success = (resp) => { 14 this responselistener emit("success", resp); 15 } 16 / 17 error method that cloud functions expect 18 / 19 this responsestub error = (resp) => { 20 this responselistener emit("error", resp); 21 } 22 / 23 listens for errors and successes from stub and returns promise that resolves or rejects accordingly 24 / 25 this resolver = new promise((resolve, reject) => { 26 this responselistener on("success", (resp) => resolve(resp)); 27 this responselistener on("error", (err) => reject(err)); 28 }); 29 } 30 31 / 32 reeturns stub to feed to cloud function 33 / 34 responsestub prototype getstub = function () { 35 return this responsestub; 36 } 37 38 / 39 returns promise that will indicate the success or failure 40 / 41 responsestub prototype oncomplete = function () { 42 return this resolver; 43 } 44 45 module exports = responsestub; โดยสรุป ฟังก์ชันสร้างในภาษา javascript นี้จะให้วิธีการสำหรับการทดสอบของเราในการส่งวัตถุการตอบสนองซึ่งระบุโดยการแก้ไข / ปฏิเสธ promise ว่าฟังก์ชันคลาวด์จะส่งคืนความสำเร็จหรือข้อผิดพลาด การทำความสะอาดฐานข้อมูล เราชัดเจนว่าไม่ต้องการให้ฐานข้อมูล parse ของการทดสอบของเราถือสิ่งที่สะสมระหว่างการทดสอบ มาสร้างยูทิลิตี้สำหรับการล้างตารางฐานข้อมูลที่สามารถเรียกใช้ก่อน (หรือหลัง) กรณีทดสอบ เพิ่มสิ่งต่อไปนี้ไปยัง ‘spec/utils/purge parse table js’ 1 // spec/utils/purge parse table js 2 / 3 removes all rows from the parse database 4 @param {string} tablename the name of the parse table to be purged 5 @return {promise} promise to destroy each item in the table 6 / 7 module exports = function (parse) { 8 return (tablename) => { 9 var tablequery; 10 if (tablename === "user") 11 tablequery = new parse query(parse user); 12 else tablequery = new parse query(tablename); 13 return tablequery find({usemasterkey true}) then((items) => { 14 var destroyqueue = \[]; 15 for (var i=0; i\<items length; i++) { 16 destroyqueue push(items\[i] destroy({usemasterkey true})); 17 } 18 return promise all(destroyqueue) catch((e) => {console log("error destroying " + e message)}); 19 }); 20 } 21 } หลังจากกำหนดฟังก์ชันนี้แล้ว เป็นเวลาที่ดีที่จะเตือนคุณให้แน่ใจว่า spec/utils/constants js ของคุณถูกกำหนดค่าให้กับแอปพลิเคชัน parse test ของคุณ ไม่ใช่แอปพลิเคชัน parse production ของคุณ สิ่งนี้จะลบข้อมูล ดังนั้นโปรดยืนยันว่านี่คือฐานข้อมูลว่างที่คุณสร้างขึ้นข้างต้น ฟังก์ชันนี้รับ parse sdk ที่เรากำหนดค่าไว้ และส่งคืนฟังก์ชันอีกฟังก์ชันหนึ่ง ฟังก์ชันที่ส่งคืนรับชื่อของตาราง และลบข้อมูลทั้งหมดจากตาราง parse ที่เกี่ยวข้อง อีกครั้ง แนวคิดในการส่งคืนฟังก์ชันอาจดูแปลก แต่ช่วยให้สเปคการทดสอบสามารถกำหนดค่า endpoint ของ parse และอ้างอิงฟังก์ชันที่จะลบตารางของ endpoint นั้นได้ ยอดเยี่ยม! ตอนนี้มาลองเขียนการทดสอบของเรากัน! 6\ ทดสอบว่า cloud function จะส่งข้อผิดพลาดหากไม่มีการส่งพารามิเตอร์ที่เหมาะสม cloud function ขึ้นอยู่กับพารามิเตอร์บางอย่างที่ต้องรวมอยู่และ ควร ล้มเหลวหาก ตัวอย่างเช่น ‘firstname’ ไม่ถูกส่ง มาทำให้แน่ใจ เราจะทำการแก้ไขไฟล์ทดสอบของเรา (ในที่สุด!) spec/signup user spec js นี่คือสิ่งที่ต้องเกิดขึ้นก่อนที่การกำหนดการทดสอบ นำเข้า parse nodejs sdk นำเข้าค่าคงที่ของเรา และกำหนดค่า parse sdk ให้ชี้ไปที่เซิร์ฟเวอร์ทดสอบของเรา นำเข้าฟังก์ชัน cloud ของเรา นำเข้าเครื่องมือ “purge table” ของเรา นำเข้าสิ่งที่เราสร้างขึ้นซึ่งเป็น response mock object สิ่งต่อไปนี้จะทำ 1 // hook into your testing server 2 var parse = require('parse/node'); 3 var constants = require(" /constants"); 4 // head over to your parse dash board for your test server, and grab your keys swap out the strings with the place holders below 5 parse initialize(constants application key, null, constants master key); 6 // if you are running a localhost parse server, set the serverurl accordingly 7 parse serverurl = 'https //parseapi back4app com' 8 var signupuser = require(" /cloud/cloud functions") signupuser(parse); 9 var purgetable = require(" /utils/purge parse table")(parse); 10 var responsestub = require(" /utils/response stub"); ตอนนี้เรามาเพิ่มกรณีทดสอบกันเถอะ การแนะนำ jasmine https //jasmine github io/2 1/introduction อาจช่วยให้เข้าใจโครงสร้างได้ดีขึ้น แต่ดูเหมือนว่าจะเป็นแบบนี้ (นำมาจากบทนำ) ดังนั้นบล็อก describe จะรวมชุดการทดสอบ และบล็อก ‘it’ จะเป็นตัวแทนของกรณีและความคาดหวัง โดยการส่งพารามิเตอร์ไปยังบล็อก ‘it’ คุณสามารถรันการทดสอบแบบอะซิงโครนัสได้ การทดสอบจะไม่เสร็จสิ้นจนกว่าพารามิเตอร์จะถูกเรียกใช้งานแบบนี้ นี่มีประโยชน์เนื่องจากการทดสอบหนึ่งในนั้นจะใช้ http ดังนั้นจึงควรทำการทดสอบแบบอะซิงโครนัสในลักษณะนี้ เนื่องจากการใช้ http เป็นกระบวนการที่ไม่บล็อกใน nodejs นอกจากนี้ jasmine ยังอนุญาตให้มีบล็อกพิเศษภายในชุดทดสอบที่สามารถทำงานในจุดต่าง ๆ ในวงจรชีวิตการทดสอบ เราต้องการลบตารางทั้งหมดก่อนการทดสอบแต่ละครั้ง ดังนั้นเราจะดำเนินการรหัสการลบในบล็อก beforeeach พูดพอแล้ว มาลงรหัสกันเถอะ! วางรหัสด้านล่างนี้ลงใน spec/signup user spec js ของคุณ ใต้การนำเข้าที่เราได้เพิ่มไว้แล้ว 1 //spec/signup user spec js 2 // imports above 3 describe("signupuser", ()=> { 4 beforeeach((done) => { 5 /// purge the user and profile tables, and then proceed 6 promise all(\[purgetable("user"), purgetable("profile")]) 7 catch((e) => fail(e)) 8 then(() => done()); 9 }); 10 it ("should reject a request to signup that does not contain all the parameters", (done) => { 11 var responsestub = new responsestub(); 12 responsestub oncomplete() 13 then(() => fail("should have failed due to invalid parameters")) 14 catch((e) => {}) 15 then(() => done()); 16 17 signupuser({ params {}}, responsestub getstub()); 18 19 }); 20 }); ยอดเยี่ยม การทดสอบครั้งแรกของเราเสร็จเรียบร้อยแล้ว ในบล็อก beforeeach เราลบตารางผู้ใช้และโปรไฟล์ จากนั้นกรณีทดสอบแรกจะถูกเรียกใช้ มันตรวจสอบว่าการส่งพารามิเตอร์ที่ไม่ถูกต้องไปยังฟังก์ชัน signupuser ทำให้ฟังก์ชันส่งข้อผิดพลาด มันใช้ response stub เพื่อให้แน่ใจว่าฟังก์ชันถูกปฏิเสธในที่สุด เพราะ ‘signupuser’ จะล้มเหลว บล็อก ‘then’ เริ่มต้นบน stub จะไม่ถูกเรียกใช้ หากมันถูกเรียกใช้ การทดสอบของเราจะล้มเหลว! ไปข้างหน้าและรันการทดสอบโดยใช้คำสั่งต่อไปนี้ คุณควรเห็นผลลัพธ์ดังต่อไปนี้ 7\ การทดสอบเกี่ยวกับการเก็บข้อมูล หวังว่าคุณจะมีการทดสอบอีกหนึ่งครั้งในตัวคุณ! เราจะตรวจสอบว่าเมื่อฟังก์ชัน cloud ของเราทำงานได้อย่างถูกต้อง ฐานข้อมูลของเราจะเป็นไปตามที่คาดหวัง จะมีโปรไฟล์หนึ่งที่มีการอ้างอิงถึงวัตถุผู้ใช้ โดยทั้งสองจะมีคุณสมบัติที่คาดหวัง เพิ่มบล็อกต่อไปนี้ลงในบล็อก ‘describe’ ที่มีอยู่ของเรา 1 //spec/signup user spec js 2 // inside describe 3 it ("should signup a user, and also create a profile that contains a reference to the user", (done) => { 4 var responsestub = new responsestub(); 5 var stub = responsestub getstub(); 6 signupuser({ 7 params { 8 firstname "john", 9 lastname "smith", 10 email "jsmith\@example com", 11 username "jsmith1", 12 password "secretcatchphrase1" 13 }, 14 }, 15 stub 16 ); 17 responsestub oncomplete() 18 then((resp) => { 19 var profileq = new parse query("profile"); 20 profileq equalto("lastname", "smith"); 21 return profileq find({usemasterkey true}); 22 }) 23 // check to make sure the profile we retrieve is valid 24 then((profiles) => { 25 if (profiles length === 0) throw new error("no profile's found"); 26 expect(profiles\[0] get('firstname')) tobe("john"); 27 // get the corresponding user 28 return profiles\[0] get("user") fetch({usemasterkey true}) 29 }) 30 // check to make sure the user is what we expect 31 then((user) => { 32 expect(user getusername()) tobe("jsmith1"); 33 }) 34 catch((e) => { 35 console log(e) 36 fail(e); 37 }) 38 then(() => done()); 39 }); โอเค นี่มันเยอะมาก ดังนั้นเรามาเดินผ่านสิ่งที่เกิดขึ้นกันเถอะ เราสร้างวัตถุจำลองการตอบสนองขึ้นมา เช่นเดียวกับในกรณีทดสอบแรก จากนั้นเราจะเรียกใช้ signupuser ด้วยการส่งคำขอที่มีพารามิเตอร์ ที่ถูกต้อง , รวมถึงการจำลองการตอบสนอง (บรรทัด 6 16) ถัดไป โค้ดนี้จะฟังสำหรับเมธอด oncomplete ของวัตถุจำลอง ซึ่งจะส่งคืน promise หากมีการเรียก response error จะทำให้ promise ถูกปฏิเสธ และจะทำให้ promise ถูกแก้ไขหากมีการเรียก response success การปฏิเสธใด ๆ จะทำให้สายของ promise ข้ามไปยังบล็อก catch ดังนั้น เมธอด fail จะถูกวางไว้ในบล็อก catch เนื่องจากการทดสอบควรล้มเหลวหาก promise ถูกปฏิเสธ การตอบสนองของ promise ควร แก้ไขเป็นวัตถุโปรไฟล์ เมื่อมันแก้ไขแล้ว เราจะทำการค้นหาสำหรับโปรไฟล์ที่มีนามสกุลเดียวกันกับที่เราสร้าง (บรรทัด 19 21) จากนั้นการทดสอบจะยืนยันว่า ‘firstname’ ของโปรไฟล์นั้นเป็นชื่อเดียวกับที่เราส่ง (บรรทัด 25 26) บล็อกถัดไปจะดึงวัตถุผู้ใช้ที่เกี่ยวข้องกับโปรไฟล์ พอยเตอร์ของวัตถุ parse จะดึงแยกกัน ดังนั้นจึงจำเป็นต้องมีบล็อก promise อีกบล็อกหนึ่ง สุดท้าย โค้ดยืนยันว่าผู้ใช้ที่เกี่ยวข้องมีชื่อผู้ใช้ที่ถูกส่งไปยังฟังก์ชัน signupuser จากนั้นการทดสอบจะเสร็จสิ้น ไปข้างหน้าและรันชุดอีกครั้ง ไปข้างหน้าและรันการทดสอบโดยใช้คำสั่งต่อไปนี้ คุณควรเห็นผลลัพธ์ต่อไปนี้ ยอดเยี่ยม! เราเขียนโค้ดคลาวด์บางส่วนและรวมกรอบการทดสอบเข้าด้วยกัน บทสรุป หากคุณหลงทางหรือเพียงแค่ต้องการโค้ดสำหรับตัวอย่างนี้ให้ไปที่ github repo https //github com/back4app/template cloud code unit test ปฏิบัติตามคำแนะนำเพื่อดาวน์โหลดและรัน หากมีสิ่งใดไม่ชัดเจนหรือไม่ทำงานโปรดติดต่อฉันทาง gmail ของฉัน jackconsidine3 ฉันหวังว่าคุณจะสนุกกับบทแนะนำนี้และได้รับข้อมูลเชิงลึกบางอย่าง!