---
title: 基于Parse Server的云代码函数单元测试指南
slug: docs-ch/cloud-code-functions/unit-tests
description: 使用Parse Server测试运行器,在不发送HTTP请求的情况下测试云代码函数,确保代码完整可靠。
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-02-28T13:56:55.364Z
---

# 如何通过他们的云代码函数创建单元测试

## 介绍

本节将允许您使用库检查函数的操作和本地测试 [parse-server-test-runner](https://github.com/AmpMe/parse-server-test-runner).

## 先决条件

:::hint{type="info"}
- 要完成本教程，您需要：

- 一个安装了 Node.js 的本地环境，以应用单元测试。您可以按照 [官方 NodeJS 教程](https://nodejs.org/en/download/package-manager/) 在您的终端成功安装 Node.js。
- 在 Back4App 创建的应用。
- 按照 [创建新应用教程](https://www.back4app.com/docs/get-started/new-parse-app) 学习如何在 Back4App 创建应用。
- 与项目配置的 Back4App 命令行工具。
- 按照 [设置云代码教程](https://www.back4app.com/docs/local-development/parse-cli) 学习如何为项目设置云代码。
:::

## 首先，我们需要谈谈单元测试！

当开发人员开始编写具有不同意图的函数时，软件社区中一个显著的观点是应用想象场景来测试创建的代码。执行单元测试程序是必要的，因为它允许您分部分测试代码，这确保了您的主代码保持完整且不受损害。

### 那么，现在来一个简单的实际例子怎么样？

假设您需要编写一个函数，以完整的句子显示工人的姓名、职位和公司。我们需要编写函数以获取以下输入项：

- 公司
- 职位
- 工人姓名

## 完成简单测试的步骤

在您的终端中，首先，我们将创建一个目录并配置您的测试应用（package.json），使用以下命令：

:::BlockQuote
$ mkdir unit-test-sample && cd unit-test-sample
$ npm init
:::

:::hint{type="warning"}
提示：
使用 <font color="#2166ae">`npm init`</font> 命令，您将能够创建 package.json 文件。它仅涵盖最常见的项目，并尝试猜测合理的默认值。因此，我们将提供代码正常运行所需的依赖项。
:::

“package.json”的结果将类似于以下示例：

```json
{
  "name": "yourfoldername",
  "version": "1.0.0",
  "description": "Just a unit test with using a simple function.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "author": "Your Name",
  "license": "ISC",
  "bugs": {
    "url": "{url}/issues"
  },
  "homepage": ""
}
```

现在，让我们使用下面的命令创建文件（index.js）:

:::BlockQuote
\~/unit-test-sample$ touch index.js
:::

我们现在将下面的代码插入到之前创建的文件中，该函数将插入一个示例，以演示此测试的代码:

```javascript
1   // index.js
2     module.exports = function(name, position, company) {
3     let sentence = "Hi, " + name + "! You are " + position + " in " + company + " company.";
4       return sentence;
5     };
```

:::hint{type="info"}
**在 NodeJS 中，模块将相关代码封装成一个单元，当你使用 *module.exports*, 它增加了可以在其他文件中使用的封装代码。**
:::

### 让我们测试 0/

最后，你可以使用你的终端:

:::BlockQuote
\~/unit-test-sample$ node
\> var moduleBack4App = require('./index.js');
undefined
\> moduleBack4App("Jonathan", "Developer", "Back4App")
嗨，Jonathan！你是Back4App公司的开发者。
:::

:::hint{type="warning"}
提示:&#x20;使用 <font color="#2166ae">`require()`</font> 函数，你可以导入和导出模块，在上述情况下，我们使用此函数在应用程序内部要求一个文件。
:::

# 使用 Parse，我可以测试我的函数吗？

当然，作为测试参数，我们将创建一个后端来控制公司员工的信息。

# 让我们将类结构化为:

### Parse.User（员工参考）

- 用户名，电子邮件，密码（必填）

我们建议你详细查看 [Parse Server](http://docs.parseplatform.org/js/guide/#users) 指南，以获取有关用户属性的更多信息。

### infoEmployee

- 职位
- 部门
- 工作班次
- 用户ID（指针）

## 1 - 理解我们的最终结构

让我们开始吧！我们将使用 [Parse Server Javascript 指南](https://www.back4app.com/docs/cloud-code-functions/docs.parseplatform.org/js/guide/) 作为我们函数开发的参数。首先，在使用命令行界面完成设置后 [（见前提条件](https://www.back4app.com/docs/cloud-code-functions/unit-tests#content-prerequisites)），我们将理解它如何与文件中的最终结构一起工作：

:::BlockQuote
├──Back4AppProject
│  ├── cloud
│  │   ├── functions.js
│  │   ├── main.js
│  ├── public
│  ├── package.json
│  ├── index.js
│  ├── node\_modules
│  ├── src
│  │   ├── jasmine.js
:::

:::hint{type="info"}
**注意：&#x20;**&#x5F53;您将文件上传到云代码时，命令行界面（见 [前提条件](https://www.back4app.com/docs/cloud-code-functions/unit-tests#content-prerequisites)）将忽略其他文件，仅上传公共和云文件夹中的文件。
:::

## 2 - 编写我们的函数

在为命令行界面配置环境后，我们将编写函数以构建注册员工并保存附加信息的过程。通过重构代码，在 main.js 文件中，我们将这些函数导入到 main 中，如下所示：

```javascript
1   //In 1cloud/main.js
2
3   var cloudFunctions = require("./functions");
4
5   /* It's necessary to insert the Parse Instance in our code,
6   because at local context not is referenced.*/
7
8   Parse.Cloud.define("registerUser",  cloudFunctions.registerUser(Parse));
9
10  Parse.Cloud.beforeSave("infoEmployee", infoEmployee.infoEmployee(Parse));
```

这个想法是将功能与云接口解耦，以便我们可以在不低效发送HTTP请求的情况下测试它们。这在我们创建测试套件时将非常有意义。

:::CodeblockTabs
Parse Server 3.X

```javascript
1   //In cloud/functions.js
2
3   module.exports.registerUser = function(Parse){
4     return async(request) =>{
5       let params = request.params; //Parameters received  
6       let infoEmployee = Parse.Object.extend("infoEmployee"); //Store Information      
7
8       let userCreated = new Parse.User({
9         "email" : params.email,
10        "username": params.username,
11        "password" : params.password
12      })
13
14      //Save relation
15      try {
16        let result = await userCreated.save();
17
18        let information = new infoEmployee({
19          "position"   : params.position,
20          "department" : params.department,
21          "workShift"  : params.shift,
22          "user" : result
23        });
24
25        return information.save();
26      } catch (e) {
27          return e.message;
28      }
29    }
30  }
31
32  module.exports.infoEmployee = function(Parse){
33    return async (request) =>{
34      let req = request.object;
35
36      if (!req.get("position") || !req.get("department") || !req.get("workShift")) {
37        throw new Error("Missing params! The required parameters are: position, department. workShift");
38      } else {
39        return;
40      }
41    }
42  }
```

Parse Server 2.X

```javascript
1   //In cloud/functions.js
2
3   module.exports.registerUser = function(Parse){
4     return function (request, response){
5       var params = request.params; //Parameters received
6
7       var infoEmployee = Parse.Object.extend("infoEmployee"); //Store Information      
8
9       var userCreated = new Parse.User({
10        "email" : params.email,
11        "username": params.username,
12        "password" : params.password
13      })
14
15      //Save relation
16      userCreated.save().then((updatedUser) => {
17        var information = new infoEmployee({
18          "position"   : params.position,
19          "department" : params.department,
20          "workShift"  : params.shift,
21          "user" : updatedUser
22        });
23        return information.save();
24      }).then((info) => response.success(info))
25        .catch((e) => {
26          response.error(e.message);
27        })
28    }
29  }
30
31  module.exports.infoEmployee = function(Parse){
32    return function (request, response){
33      var req = request.object;
34
35      if (!req.get("position") || !req.get("department") || !req.get("workShift")) {
36        response.error("Missing params! The required parameters are: position, department. workShift");
37      } else {
38        response.success();
39      }
40    }
41  }
```
:::

## &#x20;3 - 设置环境以测试上述代码！

对于我们的测试套件，我们将使用 [Jasmine](https://jasmine.github.io/), 一个非常流行的JavaScript测试框架。然而，到目前为止，我们的代码完全与我们的测试无关，因此您可以使用您喜欢的任何框架或平台。

### 安装开发依赖

让我们全局安装 Jasmine，并使用以下命令:

:::BlockQuote
\~/Back4AppProject$ sudo npm install -g jasmine
\~/Back4AppProject$ jasmine
随机种子 48094
开始


未找到规范
在 0.002 秒内完成
不完整: 未找到规范
:::

## 4 - 在文件夹中配置 Parse Server 测试运行器

在我们在Back4App的项目Cloud文件夹中实施的方法下，我们将在Node.js项目中创建新文件，以配置这种交互。

:::BlockQuote
\~/Back4AppProjectsrc$ touch index.js
\~/Back4AppProject$ mkdir src && cd src
\~/Back4AppProject/src$ touch jasmine.js
:::

现在，我们将使用下面显示的代码配置上述创建的文件：

:::CodeblockTabs
index.js

```javascript
1   const Promise = require('bluebird');
2     const express = require('express');
3     const http = require('http');
4     const {MongoClient} = require('mongodb');
5     const {ParseServer} = require('parse-server');
6
7     const mongoDBRunnerStart = require('mongodb-runner/mocha/before').bind({
8      timeout() {
9       },
10      slow() {
11      },
12    });
13    const mongoDBRunnerStop = require('mongodb-runner/mocha/after');
14
15    const startDB = () => (
16      new Promise((done, reject) => {
17        done.fail = reject;
18        mongoDBRunnerStart(done);
19      })
20    );
21
22    const stopDB = () => (
23      new Promise((done, reject) => {
24        done.fail = reject;
25        mongoDBRunnerStop(done);
26      })
27    );
28
29    const connectDB = (databaseURI) => new Promise((resolve, reject) => {
30      MongoClient.connect(databaseURI, (err, db) => {
31        if (err) {
32          reject(err);
33        } else {
34          resolve(db);
35        }
36      });
37    });
38
39    let parseServerState = {};
40
41    const dropDB = () => {
42      const {mongoConnection} = parseServerState;
43      return mongoConnection.dropDatabaseAsync();
44    };
45
46    /**
47      * Starts the ParseServer idropDatabaseAsyncnstance
48      * @param {Object} parseServerOptions Used for creating the `ParseServer`
49      * @return {Promise} Runner state
50     */
51    function startParseServer(parseServerOptions = {}) {
52      const mongodbPort = process.env.MONGODB_PORT || 27017;
53      const {
54        databaseName = 'parse-test',
55        databaseURI = 'mongodb://localhost:${mongodbPort}/${databaseName}',
56        masterKey = 'test',
57        javascriptKey = 'test',
58        appId = 'test',
59
60        port = 30001,
61        mountPath = '/1',
62        serverURL = 'http://localhost:${port}${mountPath}',
63      } = parseServerOptions;
64
65      return startDB()
66        .then(() => connectDB(databaseURI))
67        .then((mongoConnection) => {
68         parseServerOptions = Object.assign({
69            masterKey, javascriptKey, appId,
70            serverURL,
71            databaseURI,
72            silent: process.env.VERBOSE !== '1',
73          }, parseServerOptions);
74          const app = express();
75          const parseServer = new ParseServer(parseServerOptions);
76
77          app.use(mountPath, parseServer);
78
79          const httpServer = http.createServer(app);
80
81          Promise.promisifyAll(httpServer);
82          Promise.promisifyAll(mongoConnection);
83
84          return httpServer.listenAsync(port)
85            .then(() => Object.assign(parseServerState, {
86              parseServer,
87              httpServer,
88              mongoConnection,
89              expressApp: app,
90              parseServerOptions,
91            }));
92        });
93    }
94
95  /**
96   * Stops the ParseServer instance
97   * @return {Promise}
98   */
99    function stopParseServer() {
100     const {httpServer} = parseServerState;
101     return httpServer.closeAsync()
102       .then(stopDB)
103       .then(() => parseServerState = {});
104   }
105 
106   module.exports = {
107     dropDB,
108     startParseServer,
109     stopParseServer,
110     parseServerState,
111   };
```

jasmine.js

```javascript
1   const { startParseServer, stopParseServer, dropDB } = require('parse-server-test-runner');
2
3   describe('registerUser', () => {
4      beforeAll((done) => {
5       const appId = 'test';
6       const masterKey = 'test';
7       const javascriptKey = 'test';
8
9       startParseServer({ appId, masterKey, javascriptKey })
10        .then(() => {
11          Parse.initialize(appId, masterKey, javascriptKey);
12          Parse.serverURL = 'http://localhost:30001/1';
13        })
14        .then(done).catch(done.fail);
15    }, 300 * 60 * 2);
16
17    afterAll((done) => {
18      stopParseServer()
19        .then(done).catch(done.fail);
20    });
21
22    beforeEach((done) => {
23      dropDB()
24        .then(done).catch(done.fail);
25    });
26
27    it('should work', (done) => {
28      const q = new Parse.Query('_User')
29      q.limit(5)
30        .find({ useMasterKey: true })
31        .then(console.log)
32        .then(done).catch(done.fail);
33    });
34  });
```
:::

最后一步是配置 package.json，使用命令: <font color="#2166ae">`$ npm init`</font> 在根目录中（下面的文件只是一个包含所需模块的示例）:

我们将开始配置本地测试，为此我们将按照下面的命令以编程方式设置代码以进行测试。

:::BlockQuote
\~/Back4AppProject$ # 在package.json的同一目录中
\~/Back4AppProject$ npm install
:::

成功安装后，您可以使用描述的命令在本地检查单元测试并接收结果，例如：

:::BlockQuote
\~/Back4AppProject$ jasmine src/jasmine.js
随机种子 79055
开始
✔  下载了 MongoDB 3.6.5
\[]
.


1 个规范，0 个失败
在 19.983 秒内完成
随机种子 79055 (jasmine --random=true --seed=79055)
:::




## 太好了，它准备好了！

根据上述指南，您可以使用 Parse Server 测试运行器和为 Back4App 开发的云代码功能。
