Advanced Guides
Back4AppでのParse Cloudコードテストの統合方法
26 分
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 は広大な分野です。テストについて哲学的に話すのではなく、実装を通じていくつかの戦略(例えばスタブ)について話しましょう。 前提条件 このチュートリアルを完了するには、次のものが必要です: back4appにアプリがあること。 次の 新しいアプリを作成するチュートリアル をフォローして、back4appでアプリを作成する方法を学びます。 プロジェクトに設定されたback4appコマンドライン 次の cloud codeの設定チュートリアル をフォローして、プロジェクトのためにクラウドコードを設定する方法を学びます。 コマンドラインにnpmがインストールされていること 注意:このライブラリは、 https //www promisejs org/ を使用しますが、あまり複雑ではないはずです。 基本的なソーシャルメディアバックエンドを作成しましょう はい!ユーザーモデルに合わせてプロファイルモデルを含むソーシャルメディアアプリケーションを想像してみてください。一部のアプリでは、ユーザーモデルにプロファイル情報を配置することがありますが、多くの場合、これは効率的ではありません。認証/認可の関心をユーザーコンテンツから分離する必要があり、したがって2つの異なるモデルを維持する必要があります。 このチュートリアルでは、ユーザーとプロファイルの作成を管理する機能を実装し、クライアントに最小限の負担をかける方法を説明します。さあ、始めましょう! 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に対して2つの別々のリクエストを行うことを含んでおり、フロントエンドにとっては非効率的です。可能な限り、複数ステップのクライアント サーバー通信フローを避けることが賢明です。 また、セキュリティに関しては、上記のコードは作成プロセスをクライアントの手に委ねており、これは決して賢明ではありません。私たちは、データの整合性がクライアントがフローのすべてのステップを適切に完了することに依存することを防ぎたいと考えています。例えば、プロファイルなしでユーザーを作成するカスタムリクエストを送信することができ、アプリの永続データを破損させる可能性があります。 クラウドコードを使用してこれをすべて1ステップで行うのはどうでしょうか?これにより、フロントエンドコードの膨張を防ぎ、クライアントが不必要または不安全な作業を行わないことを保証できます! サインアップのためにクライアントから代わりに行いたいことは次のとおりです 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はまた、ユーザーがサインアップするときにプロファイルを作成できるように、 unhandled content type http //docs parseplatform org/cloudcode/guide/#beforesave 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\ ディレクトリ構造のリファクタリング 素晴らしい、私たちは2つのクラウド関数を作成しました。これらの関数を実行してparse dashboardを確認することでテストすることは明らかですが、それはスケーラブルでも効率的でもありません。代わりに、継続的に実行できるメソッド専用の自動テストを作成したいと考えています。したがって、コードを少し分離します。 main jsで作成した関数をcloud functions jsという新しいファイルに移動します(同じディレクトリ内)。次に、これらの関数を import し、cloud functionの定義にバインドします。アイデアは、関数をクラウドインターフェースから「デカップリング」し、非効率的にhttpリクエストを送信することなくテストできるようにすることです。これは、テストスイートを作成する際に非常に理にかなっています。 関数ファイルを作成する node jsでは、他のファイルから関数、オブジェクト、変数を引き込むために「require」を使用できることをご存知かもしれません。 したがって、ステップ1で作成したparse cloud functionに対応する関数を定義します。 1つの混乱を招く可能性のある点は、定義している関数が 関数を返す , それがparse cloudの定義にフックされることです。関数を返すために関数を使用するのは奇妙に思えるかもしれませんが、これは後でテストを書くときにparseサーバーを入れ替える力を与えてくれます。 cloud code内で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環境の統合)をインストールしましょう。 さて、テストスイートで使用する2つのライブラリをインストールしましょう。parse sdkを使用して偽のparseサーバーに接続し、リクエストオブジェクトをスタブするためにeventsライブラリを使用します。 では、jasmineユーティリティを使用して、テストディレクトリを初期化しましょう。 もしよろしければ、次のコマンドでjasmineをグローバルにインストールできます。 $ npm install g jasmine $ npm install g jasmine その後、次のコマンドで初期化できます。 $ jasmine init $ jasmine init このガイドでは、jasmineをグローバルにインストールしないことを前提としますが、推奨されています。もしインストールした場合は、すべての‘/node modules/jasmine/bin/jasmine js’のインスタンスを単に‘jasmine’に置き換えることができます。 これにより、specという名前のディレクトリが作成され、その中にjasmineの設定情報を含むsupportフォルダーが含まれます。 デフォルトでは、jasmineは「 spec js」拡張子で終わるファイルを探すことを知っているので、テストにそれに応じた名前を付けます。 最初のユニットテスト用のファイルを作成します テストを助けるために、2つのファイルを持つutilitiesディレクトリを追加します 最後に、同じディレクトリに定数ファイルを作成します。このファイルのユーティリティについては後で説明します。 これが現在のディレクトリの見た目になります 4\ テスト用parseサーバーの切り替え parse周りのテスト 私たちのメソッドはparseサーバーを含むため、その相互作用をテストできるようにしたいと考えています。これを行う方法は2つあります a 私たちは、同じインターフェースを実装するオブジェクトを定義することで、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サーバーを設定することです。これには、parseが使用する遅いhttpレイヤーが関与しますが、データベース内のデータをテストすることも可能です。私たちのテストでは、parse sdkをインポートし、テストサーバーで構成する必要があります。 クラウドコードをテストする際にスタブできる2つの場所:a ) httpリクエストを行わないparse sdkをスタブする、またはb ) テストデータベースの実装を入れ替える これらのアプローチのどちらも「正しい」答えではありません。何をテストしようとしているかによります。parse sdkのインターフェースをスタブすること(使用している部分だけでも)は多くの作業です。さらに、この例ではデータの保存後の永続性をテストする予定なので、2番目のアプローチを使用します。 やりましょう back4appでテストparseサーバーを作成する アプリケーションidとマスターキーを取得し、それらを定数ファイルに保存する テストサーバーを使用するように、specファイルでparse sdkを初期化する テスト用にローカルの parse server https //github com/parse community/parse server を実行することもできます。私たちは単にダッシュボードに別のback4appアプリケーションを作成します。 別のback4appサーバーをプロビジョニングする方法についてリフレッシュが必要な場合は、 新しいアプリを作成するチュートリアル https //www back4app com/docs/get started/new parse app を参照してください。アプリケーションに好きな名前を付けてください。ただし、testbackendのような名前を使用するのが賢明かもしれません。その後、ダッシュボード > アプリ設定 > セキュリティとキーからアプリケーションidとマスターキーを取得してください。 これらのトークンを定数ファイルに次のように保存します 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 } 本番アプリのアプリケーションidとマスターキーを絶対に入力しないでください!!! データを削除することになるので、そうするとデータを失うリスクがあります 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 ); リクエストの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があなたのtest parseアプリケーションに設定されていることを確認する良いタイミングです。生産用のparseアプリケーションではありません。これはデータを削除するので、上記で作成した空のデータベースであることを確認してください。 この関数は、設定されたparse sdkを受け取り、別の関数を返します。返された関数はテーブル名を受け取り、対応するparseテーブルからすべてのデータを削除します。 再度、関数を返すというアイデアは奇妙に思えるかもしれませんが、テスト仕様がparseエンドポイントを設定し、そのparseエンドポイントのテーブルをクリアする関数を参照できるようにします。 素晴らしい!さあ、テストを書きましょう! 6\ 適切なパラメータが渡されない場合、クラウド関数がエラーを送信することをテストします クラウド関数は特定のパラメータが含まれていることに依存しており、 送信されなかった場合、例えば‘firstname’が送信されなかった場合、 失敗する必要があります。確認しましょう。 私たちはついにテストファイルspec/signup user spec jsを編集します。 テスト定義の前に必要なことは次のとおりです parse nodejs sdkをインポートする 定数をインポートし、parse sdkをテストサーバーを指すように設定する クラウド関数をインポートする 「purge table」ユーティリティをインポートする 作成したレスポンスモックオブジェクトをインポートする 次のことを行えば大丈夫です 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’ブロックにパラメータを渡すことで、非同期にテストを実行できます。パラメータがこのように呼び出されるまで、テストは完了しません これは便利です。なぜなら、私たちのテストの1つは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ブロックでは、userとprofileテーブルをパージします。次に、最初のテストケースがトリガーされます。これは、signupuser関数に無効なパラメータを渡すと、関数がエラーを送信することを確認します。 これは、関数が最終的に拒否されたことを確認するためにレスポンススタブを使用します。‘signupuser’が失敗するため、スタブの最初の‘then’ブロックは呼び出されるべきではありません。もし呼び出されると、私たちのテストは失敗します! 次のコマンドを使用してテストを実行してください 次の出力が表示されるはずです 7\ データ永続性に関するテスト もう一つテストができることを願っています!私たちは、cloud functionが正しく実行されるとき、データベースが期待通りになることを確認します:プロファイルが存在し、ユーザーオブジェクトへの参照があり、両方とも期待される属性を持っています。 次のブロックを既存の「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を返します。レスポンスエラーが呼ばれた場合、promiseは拒否され、レスポンス成功が呼ばれた場合は解決されます。拒否が発生すると、promiseのチェーンはキャッチブロックにスキップします。したがって、promiseが拒否された場合、テストは失敗するべきなので、failメソッドはキャッチブロックに配置されています。 promiseのレスポンスは、プロファイルオブジェクトに解決されるべきです。解決されると、作成したのと同じ姓のプロファイルをクエリします(行19 21)。次に、テストはプロファイルの「firstname」が渡したものと同じであることを確認します(行25 26)。 次のブロックは、プロファイルに関連付けられたユーザーオブジェクトを取得します。parseオブジェクトポインタは別々に取得されるため、別のpromiseブロックが必要です。 最後に、コードは対応するユーザーがsignupuser関数に渡されたユーザー名を持っていることを確認します。これでテストは終了です。 もう一度スイートを実行してください: 次のコマンドを使用してテストを実行してください 次の出力が表示されるはずです 素晴らしい!私たちはいくつかのクラウドコードを書き、テストフレームワークを統合しました。 結論 もし迷った場合や、この例のコードが必要な場合は、次の githubリポジトリ https //github com/back4app/template cloud code unit test にアクセスしてください。指示に従ってダウンロードして実行してください。 何か不明な点や動作しない場合は、私のgmail、jackconsidine3までご連絡ください。 このチュートリアルを楽しんでいただけたことを願っていますし、何か洞察を得られたことを願っています!