Advanced Guides
Полное руководство по тестированию Parse Cloud Code
34 мин
как интегрировать тесты в ваши функции parse cloud code введение это руководство написано https //github com/considine , нашим гостевым автором и ведущим разработчиком в https //koptional com/ в этом учебном пособии рассматривается, как настроить автоматизированные тесты для вашего cloud code на back4app мы кратко поговорим о переносе части вашего клиентского кода 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 command line, настроенный с проектом следуйте за учебником по настройке 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, что неэффективно для фронтенда; разумно избегать многоступенчатых потоков клиент сервер, когда это возможно кроме того, что касается безопасности, приведенный выше код передает процесс создания в руки клиента, что никогда не является разумным мы хотели бы предотвратить зависимость целостности наших данных от того, что клиент правильно завершит все этапы потока например, они могут отправить пользовательский запрос, который создает пользователя без профиля, что приведет к повреждению постоянных данных приложения почему бы не сделать это все в один шаг, используя облачный код? это может предотвратить раздувание кода фронтенда и гарантировать, что клиент не выполняет ненужную/незащищенную работу! вот что мы хотим сделать вместо этого от клиента для регистрации 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 триггеры, позволяя создавать профиль, когда пользователь регистрируется однако, используя функцию, мы можем интуитивно передать атрибуты firstname и lastname, которые будет использовать профиль функция регистрации облачного кода давайте начнем! перейдите в каталог вашего проекта, который синхронизирован с back4app (см предварительные требования, если вы не знаете, что это значит) мы предположим следующую структуру в нашем случае, при инициализации, мы выбрали «облако» в качестве имени нашего каталога ваш каталог может называться как угодно 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, но это не масштабируемо и неэффективно вместо этого мы хотим создать автоматизированные тесты специально для методов, которые могут выполняться непрерывно поэтому мы немного разделим наш код мы переместим функции, которые мы создали в 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)); отлично! мы не изменили функциональность с первого шага, но мы отделили функцию от облачного кода на следующем шаге мы создадим модульный тест! 3\ создайте тестовый набор для нашего тестового набора мы будем использовать jasmine https //jasmine github io/ , популярный фреймворк для тестирования однако наш код до сих пор полностью независим от наших тестов, поэтому вы можете использовать любой фреймворк или платформу, которые вам нравятся давайте установим jasmine и jasmine node (интеграцию jasmine и нашей среды node js) теперь давайте установим две библиотеки, которые будет использовать наш тестовый набор он будет использовать parse sdk для подключения к фейковому серверу parse и библиотеку событий для имитации объекта запроса теперь, используя утилиту 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”, поэтому мы назовем наши тесты соответственно создайте файл для нашего первого юнит теста добавьте директорию утилит с двумя файлами, которые помогут с нашими тестами наконец, создайте файл констант в той же директории утилита для этого файла будет объяснена позже вот как теперь должна выглядеть ваша директория 4\ замена на тестовый сервер parse тестирование с parse поскольку наши методы связаны с сервером parse, мы хотим иметь возможность тестировать это взаимодействие существует два способа сделать это 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, который будет служить только для тестовых данных это будет включать медленный http слой, который использует parse, но также позволит нам тестировать данные в базе данных в наших тестах нам нужно будет импортировать sdk parse и настроить его с тестовым сервером два места, которые можно замокировать при тестировании облачного кода a ) замокировать sdk parse, который не будет делать http запросы, или b ) заменить реализацию тестовой базы данных ни один из этих подходов не является «правильным» ответом это зависит от того, что вы пытаетесь протестировать замокирование интерфейса для sdk parse (даже только тех частей, которые мы используем) — это много работы кроме того, мы собираемся протестировать сохранение данных после сохранения в этом примере, поэтому мы будем использовать второй подход давайте создайте тестовый сервер parse на back4app получите идентификатор приложения и мастер ключ и сохраните их в нашем файле констант инициализируйте sdk parse в нашем файле спецификаций, чтобы наш тест использовал тестовый сервер вы можете запустить локальный parse server https //github com/parse community/parse server для ваших тестов мы просто создадим еще одно приложение back4app в нашей панели управления если вам нужно освежить память о том, как создать еще один сервер back4app, перейдите к учебнику по созданию нового приложения https //www back4app com/docs/get started/new parse app назовите ваше приложение как угодно, хотя было бы разумно использовать что то вроде testbackend затем просто получите идентификатор приложения и мастер ключ из панели управления > настройки приложения > безопасность и ключи теперь сохраните эти токены в нашем файле констант следующим образом 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 } не вставляйте идентификатор приложения и мастер ключ из вашего производственного приложения!!! мы будем удалять данные, и это может привести к их потере 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, а не на ваше производственное приложение parse это удалит данные, поэтому, пожалуйста, подтвердите, что это пустая база данных, которую вы создали выше эта функция принимает наш настроенный sdk parse и возвращает другую функцию возвращаемая функция принимает имя таблицы и удаляет все данные из соответствующей таблицы 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’, вы можете запускать тесты асинхронно тест не завершится, пока параметр не будет вызван следующим образом это полезно, поскольку один из наших тестов будет использовать 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 приводит к отправке ошибки он использует заглушку ответа, чтобы убедиться, что функция в конечном итоге отклонена поскольку ‘signupuser’ завершится неудачей, начальный блок ‘then’ на заглушке не должен быть вызван если он будет вызван, наш тест провалится! продолжайте и запустите тест, используя следующее вы должны увидеть следующий вывод 7\ тест на постоянство данных надеюсь, у вас есть еще один тест! мы собираемся проверить, что когда наша облачная функция работает правильно, наша база данных будет соответствовать ожиданиям профиль будет существовать, с ссылкой на объект пользователя, оба с ожидаемыми атрибутами добавьте следующий блок в наш существующий блок ‘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 будет отклонен, если будет вызван response error, и разрешен, если будет вызван 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 надеюсь, вам понравился этот учебник и вы получили некоторые знания!