mysql不能做端点测试吗

I've been playing around with testing lately. One thing I tried to do was to test the endpoints of my Express application.

我最近一直在进行测试。 我尝试做的一件事是测试Express应用程序的端点。

Setting up the test was the hard part. People who write about tests don't actually teach you how they set it up. I could not find any useful information about this, and I had to try and figure it out.

设置测试是困难的部分。 撰写测试的人实际上并没有教您如何设置测试。 我找不到有关此的任何有用信息,因此我不得不尝试找出来。

So today, I want to share the setup I created for myself. Hopefully, this can help you when you create your own tests.

所以今天,我想分享我为自己创建的设置。 希望这对您创建自己的测试有帮助。

目录 (Table of Contents)

  1. Setting up Jest and Supertest

    设置笑话和超级测试

  2. Connecting Jest and Mongoose

    开玩笑和猫鼬

  3. Seeding a database

    播种数据库

设置笑话和超级测试 (Setting up Jest and Supertest)

First, let's talk about the stack.

首先,让我们谈谈堆栈。

堆栈 (The Stack)

  • I created my app with Express.我使用Express创建了我的应用。
  • I used Mongoose to connect to MongoDB我用Mongoose连接到MongoDB
  • I used Jest as my test framework.我使用Jest作为测试框架。

You might have expected Express and Mongoose because everyone else seems to use those two frameworks. I used them too.

您可能会想到Express和Mongoose,因为其他所有人似乎都在使用这两个框架。 我也用过

But why Jest and not other test frameworks?

但是为什么要开玩笑而不是其他测试框架呢?

为什么开玩笑 (Why Jest)

I don't like Facebook, so I didn't want to try anything that was created by Facebook's team. I know it sounds silly, but that was the truth.

我不喜欢Facebook,所以我不想尝试Facebook团队创建的任何内容。 我知道这听起来很傻,但这是事实。

Before Jest, I tried out all sorts of test frameworks. I tried Tap, Tape, Mocha, Jasmine, and AVA. Each test framework has its own pros and cons. I almost ended up with AVA, but I didn't go with AVA because I found it hard to set up. Eventually, I tried Jest out because Kent C. Dodds recommended it.

在Jest之前,我尝试了各种测试框架。 我尝试了Tap,Tape,Mocha,Jasmine和AVA。 每个测试框架都有自己的优缺点。 我差点以AVA告终,但我并没有选择AVA,因为我发现它很难设置。 最终,我尝试了Jest,因为Kent C. Dodds推荐了它。

I fell in love with Jest after trying it out. I love it because:

试用后,我爱上了Jest。 我喜欢它,因为:

  1. It's easy to setup设置容易
  2. The watch-mode is amazing

    手表模式很棒

  3. When you console.log something, it actually shows up without any difficulty (this was a bitch with AVA).

    当您进行console.log ,它实际上可以毫无困难地显示出来(这对AVA来说是个bit子)。

设置笑话 (Setting up Jest)

First, you need to install Jest.

首先,您需要安装Jest。

npm install jest --save-dev

Next, you want to add tests scripts to your package.json file. It helps to add the test and test:watch scripts (for one-off testing and watch-mode respectively).

接下来,您要将测试脚本添加到package.json文件。 它有助于添加testtest:watch脚本(分别用于一次性测试和监视模式)。

"scripts": {"test": "jest","test:watch": "jest --watch"
},

You can choose to write your test files in one of the following formats. Jest picks them up for you automatically.

您可以选择以以下一种格式编写测试文件。 笑话会自动为您挑选。

  1. js files in the __tests__ folder

    __tests__文件夹中的js文件

  2. files named with test.js (like user.test.js)

    test.js命名的文件(例如user.test.js )

  3. files named with spec.js (like user.spec.js)

    spec.js命名的文件(例如user.spec.js )

You can place your files however you like. When I tested endpoints, I put the test files together with my endpoints. I found this easier to manage.

您可以随意放置文件。 在测试端点时,我将测试文件与端点放在一起。 我发现这更易于管理。

- routes|- users/|- index.js|- users.test.js

编写您的第一个测试 (Writing your first test)

Jest includes describe, it and expect for you in every test file. You don't have to require them.

Jest在每个测试文件中都包含describeit以及对您的expect 。 您不必require它们。

  • describe lets you wrap many tests together under one umbrella. (It is used for organizing your tests).

    describe使您可以将多个测试包装在一起。 (用于组织测试)。

  • it lets you run a test.

    it可以让您进行测试。

  • expect lets you perform assertions. The test passes if all assertions passes.

    expect使您可以执行断言。 如果所有断言都通过,则测试通过。

Here's an example of a test that fails. In this example, I expect that 1 should be strictly equal to 2. Since 1 !== 2, the test fails.

这是测试失败的示例。 在此示例中,我expect 1应该严格等于2 。 由于1 !== 2 ,因此测试失败。

// This test fails because 1 !== 2
it("Testing to see if Jest works", () => {expect(1).toBe(2);
});

You'll see a failing message from Jest if you run Jest.

如果您运行Jest,则会看到来自Jest的失败消息。

npm run test:watch

You can make the test pass by expecting 1 === 1.

您可以通过期望1 === 1来使测试通过。

// This passes because 1 === 1
it("Testing to see if Jest works", () => {expect(1).toBe(1);
});

This is the most basic of tests. It's not useful at all because we haven't testing anything real yet.

这是最基本的测试。 它根本没有用,因为我们还没有测试任何真实的东西。

异步测试 (Asynchronous tests)

You need to send a request to test an endpoint. Requests are asynchronous, which means you must be able to conduct asynchronous tests.

您需要发送一个测试端点的请求。 请求是异步的,这意味着您必须能够执行异步测试。

This is easy with Jest. There are two steps:

用笑话这很容易。 分两个步骤:

  1. Add the async keyword

    添加async关键字

  2. Call done when you're done with your tests

    done测试后致电done

Here's what it can look like:

如下所示:

it("Async test", async done => {// Do your async tests heredone();
});

Note: Here's an article on Async/await in JavaScript if you don't know how to use it.

注意:如果您不知道如何使用JavaScript, 这是一篇有关在JavaScript中进行异步/等待的文章 。

测试端点 (Testing Endpoints)

You can use Supertest to test endpoints. First, you need to install Supertest.

您可以使用Supertest来测试端点。 首先,您需要安装Supertest。

npm install supertest --save-dev

Before you can test endpoints, you need to setup the server so Supertest can use it in your tests.

在测试端点之前,您需要设置服务器,以便Supertest可以在测试中使用它。

Most tutorials teach you to listen to the Express app in the server file, like this:

大多数教程都教您listen服务器文件中的Express应用程序,如下所示:

const express = require("express");
const app = express();// Middlewares...
// Routes...app.listen(3000);

This doesn't work because it starts listening to one port. If you try to write many test files, you'll get an error that says "port in use".

这不起作用,因为它开始侦听一个端口。 如果您尝试编写许多测试文件,则会收到一条错误消息,指出“正在使用端口”。

You want to allow each test file to start a server on their own. To do this, you need to export app without listening to it.

您要允许每个测试文件自行启动服务器。 为此,您需要在不听app情况下导出app

// server.js
const express = require("express");
const app = express();// Middlewares...
// Routes...module.exports = app;

For development or production purposes, you can listen to your app like normal in a different file like start.js.

出于开发或生产目的,您可以在不同的文件(如start.js收听您的app

// start.js
const app = require("./server.js");
app.listen(3000);

使用超级测试 (Using Supertest)

To use Supertest, you require your app and supertest in the test file.

要使用Supertest,您需要您的应用并在测试文件中进行supertest。

const app = require("./server"); // Link to your server file
const supertest = require("supertest");
const request = supertest(app);

Once you do this, you get the ability to send GET, POST, PUT, PATCH and DELETE requests. Before we send a request, we need to have an endpoint. Let's say we have a /test endpoint.

完成此操作后,您就可以发送GET,POST,PUT,PATCH和DELETE请求。 在发送请求之前,我们需要有一个端点。 假设我们有一个/test端点。

app.get("/test", async (req, res) => {res.json({ message: "pass!" });
});

To send a GET request to /test, you use the .get method from Supertest.

要将GET请求发送到/test ,请使用Supertest中的.get方法。

it("Gets the test endpoint", async done => {// Sends GET Request to /test endpointconst res = await request.get("/test");// ...done();
});

Supertest gives you a response from the endpoint. You can test both HTTP status and the body (whatever you send through res.json) like this:

Supertest给您来自端点的响应。 您可以像这样测试HTTP状态和正文(无论通过res.json发送的res.json ):

it("gets the test endpoint", async done => {const response = await request.get("/test");expect(response.status).toBe(200);expect(response.body.message).toBe("pass!");done();
});

开玩笑和猫鼬 (Connecting Jest and Mongoose)

The hard part about testing a backend application is setting up a test database. It can be complicated.

有关测试后端应用程序的困难部分是建立测试数据库。 可能很复杂。

Today, I want to share how I setup Jest and Mongoose.

今天,我想分享一下如何设置Jest和Mongoose。

用Jest设置猫鼬 (Setting up Mongoose with Jest)

Jest gives you a warning if you try to use Mongoose with Jest.

如果您尝试将Mongoose与Jest一起使用,Jest会警告您。

If you don't want to see this error, you need to set testEnvironment to node in your package.json file.

如果您不想看到此错误,则需要将testEnvironment设置为package.json文件中的node

"jest": {"testEnvironment": "node"
}

在测试文件中设置猫鼬 (Setting up Mongoose in a test file)

You want to connect to a database before you begin any tests. You can use the beforeAll hook to do so.

您想要在开始任何测试之前连接到数据库。 您可以使用beforeAll钩子这样做。

beforeAll(async () => {// Connect to a Mongo DB
});

To connect to a MongoDB, you can use Mongoose's connect command.

要连接到MongoDB,可以使用Mongoose的connect命令。

const mongoose = require("mongoose");
const databaseName = "test";beforeAll(async () => {const url = `mongodb://127.0.0.1/${databaseName}`;await mongoose.connect(url, { useNewUrlParser: true });
});

This creates a connection to the database named test. You can name your database anything. You'll learn how to clean them up later.

这将创建与名为test的数据库的连接。 您可以为数据库命名。 稍后您将学习如何清理它们。

Note: Make sure you have an active local MongoDB Connection before you test. Your tests will fail if you don't have an active local MongoDB Connection. Read this to learn how to create a local MongoDB connection.

注意:在测试之前,请确保您具有活动的本地MongoDB连接。 如果您没有活动的本地MongoDB连接,则测试将失败。 阅读本文以了解如何创建本地MongoDB连接。

为每个测试文件创建数据库 (Creating databases for each test file)

When you test, you want to connect to a different database for each test file, because:

测试时,您希望为每个测试文件连接到不同的数据库,因为:

  1. Jest runs each test file asynchronously. You won't know which file comes first.Jest异步运行每个测试文件。 您将不知道哪个文件先出现。
  2. You don't want tests to share the same database. You don't want data from one test file to spill over to the next test file.您不希望测试共享同一数据库。 您不希望来自一个测试文件的数据溢出到下一个测试文件。

To connect to a different database, you change the name of the database.

要连接到其他数据库,请更改数据库的名称。

// Connects to database called avengers
beforeAll(async () => {const url = `mongodb://127.0.0.1/avengers`;await mongoose.connect(url, { useNewUrlParser: true });
});
// Connects to database power-rangers
beforeAll(async () => {const url = `mongodb://127.0.0.1/power-rangers`;await mongoose.connect(url, { useNewUrlParser: true });
});

发送POST请求 (Sending a POST request)

Let's say you want to create a user for your app. The user has a name and an email address. Your Mongoose Schema might look like this:

假设您要为您的应用创建用户。 用户具有名称和电子邮件地址。 您的猫鼬模式可能如下所示:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;const userSchema = new Schema({name: String,email: {type: String,require: true,unique: true}
});module.exports = mongoose.model("User", userSchema);

To create a user, you need to save the name and email into MongoDB. Your route and controller might look like this:

要创建用户,您需要将nameemail保存到MongoDB中。 您的路线和控制器可能如下所示:

const User = require("../model/User"); // Link to your user modelapp.post("/signup", async (req, res) => {const { name, email } = req.body;const user = new User({ name, email });const ret = await user.save();res.json(ret);
});

To save the user into the database, you can send a POST request to signup. To send a post request, you use the post method. To send data along with the POST request, you use the send method. In your tests, it'll look like this.

要将用户保存到数据库中,可以发送POST请求到signup 。 要发送发布请求,请使用post方法。 要将数据与POST请求一起发送,请使用send方法。 在您的测试中,它将看起来像这样。

it("Should save user to database", async done => {const res = await request.post("/signup").send({name: "Zell",email: "testing@gmail.com"});done();
});

Note: If you run this code two times, you'll get an E1100 duplicate key error. This error occurred because:

注意:如果您两次运行此代码,则会收到E1100 duplicate key error 。 发生此错误的原因是:

  1. We said the email should be unique in the Schema above.

    我们说该email在上面的架构中应该是unique的。

  2. We tried to create another user with testing@gmail.com. even though one already exists in the database. (The first one was created when you sent the first request).

    我们尝试通过testing@gmail.com创建另一个用户。 即使数据库中已经存在一个。 (第一个请求是在您发送第一个请求时创建的)。

在两次测试之间清理数据库 (Cleaning up the database between tests)

You want to remove entries from the database between each test. This ensures you always start with an empty database.

您希望在每次测试之间从数据库中删除条目。 这样可以确保您始终从空数据库开始。

You can do this with the afterEach hook.

您可以使用afterEach挂钩执行此afterEach

// Cleans up database between each test
afterEach(async () => {await User.deleteMany();
});

In this code above, we only cleared the User collection in the database. In a real scenario, you want to clear all collections. You can use the following code to do so:

在上面的代码中,我们仅清除了数据库中的User集合。 在实际情况下,您想清除所有集合。 您可以使用以下代码进行操作:

async function removeAllCollections() {const collections = Object.keys(mongoose.connection.collections);for (const collectionName of collections) {const collection = mongoose.connection.collections[collectionName];await collection.deleteMany();}
}afterEach(async () => {await removeAllCollections();
});

测试端点 (Testing the Endpoint)

Let's begin our tests. In this test, we will send a POST request to the /signup endpoint. We want to make sure:

让我们开始测试。 在此测试中,我们将向/signup端点发送一个POST请求。 我们要确保:

  1. The user gets saved to the database用户被保存到数据库
  2. The returned object contains information about the user返回的对象包含有关用户的信息

检查用户是否已保存到数据库 (Checking if the user was saved to the database)

To check whether the user gets saved into the database, you search the database for the user.

要检查是否将用户保存到数据库中,请在数据库中搜索该用户。

const User = require("../model/User"); // Link to your user modelit("Should save user to database", async done => {const res = await request.post("/signup").send({name: "Zell",email: "testing@gmail.com"});// Searches the user in the databaseconst user = await User.findOne({ email: "testing@gmail.com" });done();
});

If you console.log user, you should see something like this:

如果您是console.log用户,则应该看到以下内容:

This means our user got saved to the database. If we want to confirm the user has a name and an email, we can do expect them to be true.

这意味着我们的用户已保存到数据库。 如果我们想确认用户的姓名和电子邮件,我们可以expect它们是真实的。

it("Should save user to database", async done => {// Sends request...// Searches the user in the databaseconst user = await User.findOne({ email: "testing@gmail.com" });expect(user.name).toBeTruthy();expect(user.email).toBeTruthy();done();
});

检查返回的对象是否包含有关用户的信息 (Checking if the returned object contains the information about the user)

We want to make sure the returned object contains the user's name and email address. To do this, we check the response from the post request.

我们要确保返回的对象包含用户名和电子邮件地址。 为此,我们检查了发帖请求的响应。

it("Should save user to database", async done => {// Sends request...// Searches the user in the database...// Ensures response contains name and emailexpect(res.body.name).toBeTruthy();expect(res.body.email).toBeTruthy();done();
});

We're done with our tests now. We want to delete the database from MongoDB.

现在我们已经完成了测试。 我们要从MongoDB删除数据库。

删除数据库 (Deleting the database)

To delete the database, you need to ensure there are 0 collections in the database. We can do this by dropping each collection we used.

要删除数据库,您需要确保数据库中有0个集合。 我们可以通过删除我们使用的每个集合来做到这一点。

We'll do after all our tests have run, in the afterAll hook.

在所有测试运行之后,我们将在afterAll挂钩中进行操作。

afterAll(async () => {// Removes the User collectionawait User.drop();
});

To drop all your collections you can use this:

要删除所有收藏集,可以使用以下命令:

async function dropAllCollections() {const collections = Object.keys(mongoose.connection.collections);for (const collectionName of collections) {const collection = mongoose.connection.collections[collectionName];try {await collection.drop();} catch (error) {// This error happens when you try to drop a collection that's already dropped. Happens infrequently.// Safe to ignore.if (error.message === "ns not found") return;// This error happens when you use it.todo.// Safe to ignore.if (error.message.includes("a background operation is currently running"))return;console.log(error.message);}}
}// Disconnect Mongoose
afterAll(async () => {await dropAllCollections();
});

Finally, you want to close the Mongoose connection to end the test. Here's how you can do it:

最后,您想关闭Mongoose连接以结束测试。 方法如下:

afterAll(async () => {await dropAllCollections();// Closes the Mongoose connectionawait mongoose.connection.close();
});

That's everything you need to do to setup Mongoose with Jest!

这就是用Jest设置Mongoose所需要做的一切!

重构 (Refactoring)

There's a lot of code that goes into beforeEach, afterEach, and afterAll hooks. We will be using them for every test file. It makes sense to create a setup file for these hooks.

beforeEachafterEachafterAll挂钩中有很多代码。 我们将在每个测试文件中使用它们。 为这些挂钩创建安装文件很有意义。

// test-setup.js
const mongoose = require("mongoose");
mongoose.set("useCreateIndex", true);
mongoose.promise = global.Promise;async function removeAllCollections() {const collections = Object.keys(mongoose.connection.collections);for (const collectionName of collections) {const collection = mongoose.connection.collections[collectionName];await collection.deleteMany();}
}async function dropAllCollections() {const collections = Object.keys(mongoose.connection.collections);for (const collectionName of collections) {const collection = mongoose.connection.collections[collectionName];try {await collection.drop();} catch (error) {// Sometimes this error happens, but you can safely ignore itif (error.message === "ns not found") return;// This error occurs when you use it.todo. You can// safely ignore this error tooif (error.message.includes("a background operation is currently running"))return;console.log(error.message);}}
}module.exports = {setupDB(databaseName) {// Connect to MongoosebeforeAll(async () => {const url = `mongodb://127.0.0.1/${databaseName}`;await mongoose.connect(url, { useNewUrlParser: true });});// Cleans up database between each testafterEach(async () => {await removeAllCollections();});// Disconnect MongooseafterAll(async () => {await dropAllCollections();await mongoose.connection.close();});}
};

You can import the setup file for each test like this:

您可以像这样导入每个测试的安装文件:

const { setupDB } = require("../test-setup");// Setup a Test Database
setupDB("endpoint-testing");// Continue with your tests...

There's one more thing I want to show you.

我还要告诉你一件事。

When you create tests, you want to seed the database with fake data.

创建测试时,您想用假数据为数据库播种。

播种数据库 (Seeding a database)

When you write tests for the backend, you need to test for four different kinds of operations:

在为后端编写测试时,需要测试四种不同类型的操作:

  1. Create (for adding things to the database)创建(用于向数据库中添加内容)
  2. Read (for getting things from the database)阅读(用于从数据库获取内容)
  3. Update (for changing the database)更新(用于更改数据库)
  4. Delete (for deleting things from the database)删除(用于从数据库中删除内容)

The easiest type to test for is create operations. You put something into the database and test whether it's there.

要测试的最简单类型是创建操作。 您将某些东西放到数据库中,然后测试它是否在那里。

For the other three types of operations, you need to put something into the database before you write the test.

对于其他三种类型的操作,您需要编写测试之前将某些内容放入数据库中。

将事物放入数据库 (Putting things into the database)

The process where you add things to a database is called seeding a database.

将事物添加到数据库的过程称为播种数据库

Let's say you want to add three users to the database. These users contain a name and an email address.

假设您要将三个用户添加到数据库中。 这些用户包含名称和电子邮件地址。

const users = [{name: "Zell",email: "testing1@gmail.com"},{name: "Vincy",email: "testing2@gmail.com"},{name: "Shion",email: "testing3@gmail.com"}
];

You can use your models to seed the database at the start of the test.

您可以在测试开始时使用模型为数据库添加种子。

const User = require("../model/User"); // Link to User modelit("does something", async done => {// Add users to the databasefor (const u of users) {const user = new User(u);await user.save();}// Create the rest of your test here
});

If you need these users for every test, the best way is to add them through the beforeEach hook. The beforeEach hook runs before every it declaration.

如果每次测试都需要这些用户,最好的方法是通过beforeEach挂钩添加它们。 beforeEach挂钩在每个it声明之前运行。

// Seed the database with users
beforeEach(async () => {for (u of users) {const user = new User(u);await user.save();}
});

You can also use Mongoose's create function to do the same thing. It runs new Model() and save(), so the code below and the one above does the same thing.

您还可以使用Mongoose的create函数执行相同的操作。 它运行new Model()save() ,因此下面的代码和上面的代码执行相同的操作。

// Seed the database with users
beforeEach(async () => {await User.create(users);
});

创建与插入 (create vs insertMany)

Mongoose has a second method to help you seed the database. This method is called insertMany. insertMany is faster than create, because:

猫鼬有第二种方法可以帮助您播种数据库。 此方法称为insertManyinsertManycreate更快,因为:

  • insertMany sends one operation to the server

    insertMany向服务器发送一个操作

  • create sends one operation for each document

    create为每个文档发送一个操作

However, insertMany does not run the save middleware.

但是, insertMany不会运行save中间件。

触发保存中间件重要吗? (Is triggering the save middleware important?)

This depends on your seed data. If your seed data needs to go through the save middleware, you need to use create. For example, let's say you want to save a user's password into the database. You have this data:

这取决于您的种子数据。 如果您的种子数据需要通过save中间件,则需要使用create 。 例如,假设您要将用户的密码保存到数据库中。 您有以下数据:

const users = [{name: "Zell",email: "testing1@gmail.com",password: "12345678"},{name: "Vincy",email: "testing2@gmail.com",password: "12345678"},{name: "Shion",email: "testing3@gmail.com",password: "12345678"}
];

When we save a user's password into the database, we want to hash the password for security reasons. We usually hash the password through the save middleware.

当我们将用户的密码保存到数据库中时,出于安全原因,我们希望对密码进行哈希处理。 我们通常通过save中间件对密码进行哈希处理。

// Hashes password automatically
userSchema.pre("save", async function(next) {if (!this.isModified("password")) return next();const salt = bcrypt.genSaltSync(10);const hashedPassword = bcrypt.hashSync(password, salt);this.password = hashedPassword;
});

If you use create, you'll get users with hashed passwords:

如果使用create ,则将为用户提供带有哈希密码的用户:

If you use insertMany, you'll get users without hashed passwords:

如果使用insertMany ,您将获得没有哈希密码的用户:

何时使用create,何时使用insertMany (When to use create, when to use insertMany)

Since insertMany is faster than create, you want to use insertMany whenever you can.

由于insertMany快于create ,因此您想尽可能使用insertMany

Here's how I do it:

这是我的方法:

  1. If seed data does not require the save middleware, use insertMany.

    如果种子数据不需要save中间件,请使用insertMany

  2. If seed data requires save middleware, use create. Then, overwrite seed data so it no longer requires the save middleware.

    如果种子数据需要save中间件,请使用create 。 然后,覆盖种子数据,使其不再需要save中间件。

For the password example above, I would run create first. Then, I copy-paste the hashed password seed data. Then, I'll run insertMany from this point onwards.

对于上面的密码示例,我将首先运行create 。 然后,我将粘贴的哈希密码种子数据复制粘贴。 然后,从现在开始,我将运行insertMany

If you want to overwrite complicated seed data, you might want to get JSON straight from MongoDB. To do this, you can use mongoexport:

如果要覆盖复杂的种子数据,则可能要直接从MongoDB获取JSON。 为此,您可以使用mongoexport

mongoexport --db <databaseName> --collection <collectionName> --jsonArray --pretty --out output.json

This says:

这说:

  1. Export <collection> from <databaseName>

    <databaseName>导出<collection> <databaseName>

  2. Creates output as a JSON Array, prettified, in a file called output.json. This file will be placed in the folder where you run the command.

    在名为output.json的文件中,将输出创建为经过修饰的JSON数组。 该文件将放置在运行命令的文件夹中。

播种多个测试文件和集合 (Seeding multiple test files and collections)

You want a place to store your seed data so you can use them across all your tests and collections. Here's a system I use:

您需要一个存储种子数据的地方,以便可以在所有测试和集合中使用它们。 这是我使用的系统:

  1. I name my seed files according to their models. I seed a User model with the user.seed.js file.

    我根据种子模型命名种子文件。 我用user.seed.js文件播种了一个User模型。

  2. I put my seed files in the seeds folder

    我把种子文件放在seeds文件夹中

  3. I loop through each seed file to seed the database.我遍历每个种子文件为数据库做种子。

To loop through each seed file, you need to use the fs module. fs stands for filesystem.

要遍历每个种子文件,您需要使用fs模块。 fs代表文件系统。

The easiest way to loop through the files is to create an index.js file in the same seeds folder. Once you have the index.js file, you can use the following code to look for all files with *.seed.js

循环浏览文件的最简单方法是在同一seeds文件夹中创建一个index.js文件。 拥有index.js文件后,您可以使用以下代码查找带有*.seed.js所有文件。

const fs = require("fs");
const util = require("util");// fs.readdir is written with callbacks.
// This line converts fs.readdir into a promise
const readDir = util.promisify(fs.readdir);async function seedDatabase() {// Gets list of files in the directory// `__dirname` points to the `seeds/` folderconst dir = await readDir(__dirname);// Gets a list of files that matches *.seed.jsconst seedFiles = dir.filter(f => f.endsWith(".seed.js"));
}

Once you have a list of seed files, you can loop through each seed file to seed the database. Here, I use a for...of loop to keep things simple.

一旦有了种子文件列表,就可以遍历每个种子文件以为数据库添加种子。 在这里,我使用for...of循环使事情保持简单。

async function seedDatabase() {for (const file of seedFiles) {// Seed the database}
}

To seed the database, we need to find the correct Mongoose model from the name of the seed file. A file called user.seed.js should seed the User model. This means:

要播种数据库,我们需要从播种文件的名称中找到正确的Mongoose模型。 名为user.seed.js的文件应为User模型设定种子。 这表示:

  1. We must find user from user.seed.js

    我们必须从user.seed.js找到user

  2. We must capitalize user into User

    我们必须利用userUser

Here's a crude version that does what's required. (If you want to, you can make the code more robust with regex instead of split).

这是满足要求的原始版本。 (如果需要,可以使用regex而不是split使代码更健壮)。

for (const file of seedFiles) {const fileName = file.split(".seed.js")[0];const modelName = toTitleCase(fileName);const model = mongoose.models[modelName];
}

Next, we want to make sure each file has a Model that corresponds to it. If the model cannot be found, we want to throw an error.

接下来,我们要确保每个文件都有一个与其对应的模型。 如果找不到模型,我们想抛出一个错误。

for (const file of seedFiles) {//...if (!model) throw new Error(`Cannot find Model '${modelName}'`);
}

If there's a corresponding model, we want to seed the database with the contents in the seed file. To do this, we need to read the seed file first. Here, since I used the .js extension, I can simply require the file.

如果有相应的模型,我们希望使用种子文件中的内容为数据库播种。 为此,我们需要先读取种子文件。 在这里,由于我使用了.js扩展名,因此我只需要该文件即可。

for (const file of seedFiles) {//...const fileContents = require(path.join(__dirname, file));
}

For this to work, my seed files must export an array of data.

为此,我的种子文件必须导出数据数组。

module.exports = [{name: "Zell",email: "testing1@gmail.com",password: "12345678"},{name: "Vincy",email: "testing2@gmail.com",password: "12345678"},{name: "Shion",email: "testing3@gmail.com",password: "12345678"}
];

Once I have the contents of the seed file, I can run create or insertMany.

获得种子文件的内容后,即可运行createinsertMany

async function seedDatabase(runSaveMiddleware = false) {// ...for (const file of seedFiles) {// ...runSaveMiddleware? model.create(fileContents): model.insertMany(fileContents);}
}

Here's the whole seedDatabase code:

这是整个seedDatabase代码:

const fs = require("fs");
const util = require("util");
const readDir = util.promisify(fs.readdir).bind(fs);
const path = require("path");
const mongoose = require("mongoose");function toTitleCase(str) {return str.replace(/\w\S*/g, txt => {return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}async function seedDatabase(runSaveMiddleware = false) {const dir = await readDir(__dirname);const seedFiles = dir.filter(f => f.endsWith(".seed.js"));for (const file of seedFiles) {const fileName = file.split(".seed.js")[0];const modelName = toTitleCase(fileName);const model = mongoose.models[modelName];if (!model) throw new Error(`Cannot find Model '${modelName}'`);const fileContents = require(path.join(__dirname, file));runSaveMiddleware? await model.create(fileContents): await model.insertMany(fileContents);}
}

为什么是JS,而不是JSON? (Why JS, not JSON?)

It's the industry norm to use JSON to store data. In this case, I find it easier to use JavaScript objects because:

使用JSON来存储数据是行业规范。 在这种情况下,我发现使用JavaScript对象更加容易,因为:

  1. I don't have to write opening and closing double-quotes for each property.我不必为每个属性编写开始和结束双引号。
  2. I don't have to use double-quotes at all! (It's easier to write single-quotes because there's no need to press the shift key).我根本不需要使用双引号! (写单引号比较容易,因为不需要按Shift键)。
// Which is easier to write. JavaScript objects or JSON?// JavaScript objects
module.exports = [{objectName: "property"}
][// JSON{objectName: "property"}
];

If you want to use JSON, make sure you change seedDatabase to work with JSON. (I'll let you work through the code yourself).

如果要使用JSON,请确保更改seedDatabase以使用JSON。 (我会让您自己编写代码)。

调整setupDB功能 (Adjusting the setupDB function)

Earlier, I created a setupDB function to help set up databases for my tests. seedDatabase goes into the setupDB function since seeding is part of the setting up process.

之前,我创建了setupDB函数来帮助设置测试数据库。 由于播种是设置过程的一部分,因此seedDatabase进入setupDB函数。

async function seedDatabase(runSaveMiddleware = false) {// ...
}module.exports = {setupDB(databaseName, runSaveMiddleware = false) {// Connect to MongoosebeforeAll(/*...*/);// Seed DatabeforeEach(async () => {await seedDatabase(runSaveMiddleware);});// Cleans up database between each testafterEach(/*...*/);// Disconnect MongooseafterAll(/*...*/);}
};

Github存储库 (A Github Repository)

I created a Github repository to go with this article. I hope this demo code helps you start testing your applications.

我创建了一个Github存储库来处理本文。 我希望该演示代码可以帮助您开始测试应用程序。



Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.

谢谢阅读。 本文最初发布在我的博客上 。 如果您想要更多的文章来帮助您成为更好的前端开发人员,请注册我的时事通讯 。

翻译自: https://www.freecodecamp.org/news/end-point-testing/

mysql不能做端点测试吗

mysql不能做端点测试吗_端点测试的分步介绍相关推荐

  1. react测试组件_如何测试React组件:完整指南

    react测试组件 When I first started learning to test my apps back in the day, I would get very frustrated ...

  2. python开发测试岗_作为测试开发岗的面试官,我都是怎么选人的?

    最近一段时间面试了不少人,主要是一些测试开发岗,中高级的初级的也都有:也有一些偏业务测试岗的候选人.总结出了一些方法论,或者说更多的是个人作为面试官所遵守的一套面试准则. 1.什么是面试? 面试不仅仅 ...

  3. 测试驱动开发 测试前移_为什么测试驱动的开发有用?

    测试驱动开发 测试前移 有关如何更有效地应用TDD的技巧,以及为什么它是一种有价值的技术 (Tips on how to apply TDD more efficiently, and why it' ...

  4. 金丝雀测试实践_金丝雀测试

    金丝雀测试实践 Canary测试是最小的测试,可以快速,自动地验证您所依赖的一切是否就绪. 您在其他耗时的测试之前运行Canary测试,并且在其他测试变为红色时浪费您的时间调查代码. 如果Canary ...

  5. 3测试原理_气密性测试智能手表防水测试方案

    理论依据 •当被检测器件出现泄漏时,密闭腔体内会出现气体摩尔量的损失. •在宏观上则表现为气体压力的降低.故,我司通过精确地测量压力从而达到精密测漏的目的. •直压压损法和定量差压法依据着相同的测试原 ...

  6. mysql 统计做饼状图_使用Highcharts结合PHP与Mysql生成饼状图

    我们在做复杂的数据统计功能时会用到饼状图,饼状图用整个圆表示总体的数量或整体值1,用圆内各个扇形的大小表示各部分数量或该部分占总体的百分比,它可以清晰直观的表示各部分之间以及各部分与整体之间的数量关系 ...

  7. 模糊测试软件测试_模糊测试

    多年来,我震惊于可能导致Microsoft Word崩溃的损坏文件数量. 几个字节不合时宜,整个应用程序就大火了. 在较旧的,不受内存保护的操作系统上,整个计算机通常会随之崩溃. 为什么Word无法识 ...

  8. alphac测试和bata测试区别_绝缘电阻测试仪和接地电阻测试仪的测试方式区别

    绝缘电阻测试仪和接地电阻测试仪的测试方式区别 (1)绝缘电阻测试仪的测试方式 绝缘电阻测试仪是测试电线电缆相间.层间以及中性点之间的绝缘程度,测试数值越高,绝缘性能越好,绝缘电阻的测量可以采用UMG2 ...

  9. 声场测试话筒_麦克风测试/使用时要知道的10个重要声学知识

    麦克风测试/使用时要知道的10个重要声学知识 1.混响 声音在房间内衰减的方式是影响声音录制的重要因素.混响对声音的作用是两面的,可以更好也可以更坏,混响时间是其中重要的条件.混响时间指的是从声源停止 ...

最新文章

  1. 64位ubuntu安装WPS
  2. tensorflow tf.Variable 的用法
  3. 在swt中获取jar包中的文件 uri is not hierarchical
  4. Leetcode 415. 字符串相加
  5. 养成让自己进步的10个习惯
  6. 信安教程第二版-第15章网络安全主动防御技术与应用
  7. 剑指 Offer59-I-滑动窗口的最大值
  8. tomcat7.0支持什么版本的jdk_JDK/Java 16 可能带来什么新特性?
  9. bootstrap-suggest插件处理复杂对象时的解决方案
  10. Android Studio API 文档_下载与使用
  11. 《ASP.NET技术详解与应用实例》配套源代码下载
  12. mssql无法通过ip连接mysql_解决ADO通过指定IP地址连接到SQLServer数据库时总是失败问题...
  13. 因服务器升级维护平安京怎么办,《阴阳师》手游6月16日维护更新公告
  14. 不小心误删@‘local’操作恢复
  15. Android MVP 实践之路(理解篇)
  16. 幻立方解法之素数3阶幻立方
  17. 数据库应用——DQL查询数据(连表查询 子查询 详细案例)
  18. Android 自定义控件之画篮球
  19. Android笔记:Dialog显示图片
  20. 51假期读书笔记(下)——流畅的python

热门文章

  1. linux umount 时出现device is busy 的处理方法--fuser
  2. 【实习生笔试面试题】2013年搜狐SOHU实习生技术笔试题
  3. 初识数据流 bit byte char三者的区别 java
  4. Navicat Premium 12安装过程和相关资源
  5. dj鲜生-09-商品应用-首页的显示
  6. linux 使用命令直接查看带单位的文件大小
  7. 浏览器安全与MSAA
  8. filebeat 配置文件详解
  9. 生产环境Nginx配置文件
  10. php使用PHPMailer发送邮件示例