API测试101:用开玩笑和Supertest测试NodeJS API的初学者指南
#初学者 #node #测试 #backenddevelopment

在当今快节奏的软件开发世界中,测试已成为software development life cycle的重要组成部分,并且随着网络和移动应用程序的增加,无漏洞,可靠和有效的API对于满足各种的各种API至关重要业务需求。

本指南将为Node.js开发人员提供一个起点,他们想了解使用Jest和supertest的测试和编写API测试的基础。

我们将介绍诸如软件测试,测试类型,测试框架的类型以及基本创建,读取,更新和删除(CRUD)操作API的示例测试代码的逐步步行。

要完全遵循指南,您需要了解使用node.js以及任何node.js框架(例如Express.js,Nest.js等)构建API的基本原理。但是,您可以在“代码”部分之前遵循前几个部分,对后端开发几乎没有了解。

软件测试

首先,在继续了解API测试之前,让我们知道什么是软件测试。根据IBM上的一篇文章,软件测试是评估和验证软件产品或应用程序是否可以做的事情的过程。它涉及提供边缘案例以测试应用程序按预期工作。

什么是API测试?

这是一种软件测试形式,可以评估和验证API是否按预期发挥作用。这样做是为了测试API的安全性,性能和可靠性。它通常涉及模拟对API端点的请求,以测试响应与预期结果相匹配的响应。

API测试的重要性

根据SmartBear在2020年进行的一项调查,有90%的组织表示,他们目前有或计划在不久的将来进行正式的API测试过程。这突出了API测试的重要性及其向其转变。

A 2020 survey by smartBear illustrating the importance of API testing in business organizations

SmartBear的2020年调查说明了商业组织中API测试的重要性。

以下是API测试的更多好处:

  • 它可以确保API每次都返回正确的响应。

  • 它有助于识别安全漏洞,以防止API受到攻击者的妥协。

  • 它允许对API进行详细而准确的文档。

  • 它可以帮助识别绩效问题。

  • 它有助于输送较少错误的高质量API。

测试类型

有许多类型的测试,但是我们将研究后端开发人员的两种最常见的测试类型。

单位测试

单元测试是一种侧重于测试代码库各个组件的测试。在API开发中,单位测试用于测试单个路线,中间WARES或功能,而无需考虑与系统的其他组件(例如数据库,缓存系统,内部服务或其他外部服务)的互动。

集成测试

集成测试是一种测试逻辑分组模块的一种测试。它还专注于测试应用程序的不同组件或服务之间的相互作用。在API开发中,集成测试用于测试API路线,数据库,中间Wares或功能以及其他内部或外部服务之间的相互作用。

测试框架和库

测试框架和库是用于在软件开发中执行测试的软件应用程序或软件包。有几种用于不同目的和编程语言的测试工具和库。但是,下面列出了Node.js应用程序和API的常见测试框架和库:

  • supertest

  • Mocha

  • 超级代理

让我们看一下开玩笑和超级巨星做什么。

开玩笑Jest是一种测试框架或工具,用于为任何JavaScript代码库编写测试,包括Node.js Projects。它易于使用,有据可查,几乎没有配置,并且可以自定义以满足一个人的需求。

supertest: Supertest是一个用于测试node.js apis的测试库。它最好与Jest或Mocha之类的测试框架一起使用。

在下一个部分中,我们将研究如何使用jest和supertest测试node.js(express)api。

Express服务器设置

在本节中,我们将开发一本书API,允许从数据库中创建,检索,更新和删除书籍。

nb:本节中的代码仅是为了证明测试API的基本概念,并且绝不是如何制作现代Web API或测试或结构。

在我们继续之前,请确保您在计算机上安装了Node.js

按照以下步骤设置Express服务器:

  1. 首先,为书API创建一个新目录,然后在您的首选编辑器中打开。

  2. 导航到终端中的目录并运行npm init -y以初始化一个新的node.js项目。

  3. 在您的终端中,运行npm install express mongoose安装Express和Mongoose软件包。

  4. 现在,创建一个名为server.js的新文件,然后添加以下代码:

const express = require('express');
const mongoose = require('mongoose');

// Create a schema for your data
const bookSchema = new mongoose.Schema({
  title: String,
  author: String,
  genre: String
});

// Create a model based on the schema
const Book = mongoose.model('Book', bookSchema);

// Create an Express app
const app = express();
app.use(express.json());

app.get('/', (req, res) => {
  res.json({ text: 'Server started on port 3001' })
})

// Create a new book
app.post('/books', (req, res) => {
  const { title, author, genre } = req.body;
  const book = new Book({
    title,
    author,
    genre
  });

  book.save().then((result) => {
    res.json(result);
  }).catch((error) => {
    res.status(500).json({ error: 'Error creating book' });
  });
});

// Get all books
app.get('/books', (req, res) => {
  Book.find().then((books) => {
    res.json(books);
  }).catch((error) => {
    res.status(500).json({ error: 'Error retrieving books' });
  });
});

// Get a single book by ID
app.get('/books/:id', (req, res) => {
  const { id } = req.params;

  Book.findById(id).then((book) => {
    if (book) {
      res.json(book);
    } else {
      res.status(404).json({ error: 'Book not found' });
    }
  }).catch((error) => {
    res.status(500).json({ error: 'Error retrieving book' });
  });
});

// Update a book by ID
app.put('/books/:id', (req, res) => {
  const { id } = req.params;
  const { title, author, genre } = req.body;

  Book.findByIdAndUpdate(id, {
    title,
    author,
    genre
  }, { new: true }).then((book) => {
    if (book) {
      res.json(book);
    } else {
      res.status(404).json({ error: 'Book not found' });
    }
  }).catch((error) => {
    res.status(500).json({ error: 'Error updating book' });
  });
});

// Delete a book by ID
app.delete('/books/:id', (req, res) => {
  const { id } = req.params;

  Book.findByIdAndRemove(id).then((book) => {
    if (book) {
      res.json(book);
    } else {
      res.status(404).json({ error: 'Book not found' });
    }
  }).catch((error) => {
    res.status(500).json({ error: 'Error deleting book' });
  });
});

// Start the server
const server = app.listen(3001, async () => {
  console.log('Server started on port 3001');
  await mongoose.connect('mongodb+srv://technical-guide:uQxSkFkzgx29r3E8@test-cluster.ne5tsho.mongodb.net/book-api?retryWrites=true&w=majority', {
    useNewUrlParser: true,
    useUnifiedTopology: true
  });
});

module.exports = server;

保存文件,返回您的终端,然后运行node server.js。您应该有这样的东西:

Terminal interface showing "Connected to MongoDB"

我们成功地创建了一个基本的crud api,用express.js和mongodb。

由于本指南主要集中于测试,因此我将简要解释上述API所做的事情而不会太详细。

本书API允许用户与书籍集进行交互。它建立了与MongoDB数据库的连接,并为书籍数据定义了架构。用户可以通过发送带有本书详细信息的帖子请求来创建新书,并使用带有相应书ID的Get请求来检索所有书籍,通过其ID获取特定书籍,通过发送看台请求来更新书籍的信息具有更新的详细信息和书的ID,并使用删除请求删除书ID的书。服务器在端口3000上运行。

用开玩笑和超级款式测试API

要开始使用Jest和supertest测试API,我们需要首先通过导航到项目目录,然后运行npm install --save-dev jestnpm install --save-dev supertest首先下载两个软件包。 --save-dev标志用于将软件包作为开发依赖项安装。

接下来,创建一个名为server.test.js的文件。这是我们的测试代码所在的地方。我们使用test.js的文件扩展名保存文件,因为玩笑将此扩展名识别为测试文件。

现在,将下面的代码复制到server.test.js文件(说明简短)。

const request = require('supertest');
const app = require('./server');
const mongoose = require('mongoose');

describe('Book API Endpoints', () => {
  let bookId;

  afterAll((done) => {
    app.close(done);
    mongoose.disconnect();
  });

  it('should create a new book', async () => {
    const res = await request(app)
      .post('/books')
      .send({
        title: 'Test Book',
        author: 'Test Author',
        genre: 'Test Genre',
      });

    expect(res.statusCode).toEqual(200);
    expect(res.body.title).toEqual('Test Book');
    expect(res.body.author).toEqual('Test Author');
    expect(res.body.genre).toEqual('Test Genre');
    bookId = res.body._id;
  });

  it('should get all books', async () => {
    const res = await request(app).get('/books');
    expect(res.statusCode).toEqual(200);
    expect(Array.isArray(res.body)).toBeTruthy();
  });

  it('should get a single book by ID', async () => {
    const res = await request(app).get(`/books/${bookId}`);
    expect(res.statusCode).toEqual(200);
    expect(res.body.title).toEqual('Test Book');
    expect(res.body.author).toEqual('Test Author');
    expect(res.body.genre).toEqual('Test Genre');
  });

  it('should update a book', async () => {
    const res = await request(app)
      .put(`/books/${bookId}`)
      .send({
        title: 'Updated Book',
        author: 'Updated Author',
        genre: 'Updated Genre',
      });
    expect(res.statusCode).toEqual(200);
    expect(res.body.title).toEqual('Updated Book');
    expect(res.body.author).toEqual('Updated Author');
    expect(res.body.genre).toEqual('Updated Genre');
  });

  it('should delete a book', async () => {
    const res = await request(app).delete(`/books/${bookId}`);
    expect(res.statusCode).toEqual(200);
    expect(res.body.title).toEqual('Updated Book');
    expect(res.body.author).toEqual('Updated Author');
    expect(res.body.genre).toEqual('Updated Genre');
  });
});

让我们浏览测试代码。

我们导入必要的模块,包括请求,一种超级台式方法,它使您可以与服务器进行交互,就像它正在运行并接收实际的HTTP请求一样。我们还从server.js文件和mongoose导入app以进行数据库操作。

Jest describe功能用于分组相关的测试用例。在这种情况下,所有测试用例都与书API端点有关。

执行了测试套件中的所有测试用例后,JEST提供的afterAll挂钩用于关闭服务器并与MongoDB数据库断开。这样可以确保我们在测试后清理资源;否则,测试完成后,开玩笑就不会退出。

每个测试用例由it块表示,该块包含测试名称和测试代码。

测试用例使用Supertest向API端点提出HTTP请求,并使用期望功能检查预期结果的响应。

在第一个测试案例中,我们通过向/books端点发送帖子请求并检查响应状态代码是否为200 (OK),并且响应主体是否包含正确的书籍信息。

,我们创建了新书。

我们将bookId保存在响应中,以供以后在其他测试用例中使用。

在第二个测试用例中,我们获取了我们在上一个测试用例中创建的书。我们通过向端点/books/:id发送get请求来做到这一点,其中:id是我们在第一个测试案例中创建的书的bookId

在第三个测试案例中,我们更新本书的信息。我们通过将PUT请求发送到/books/:id端点,并使用更新的书籍数据来做到这一点。同样,我们使用我们保存在第一个测试案例中的bookId变量来定位我们之前创建的特定书籍。

在第四个测试案例中,我们删除了第一个测试案例中创建的书。我们通过使用bookId变量将删除请求发送到/books/:id端点来做到这一点。

现在,我们已经设置了所有内容,以运行我们的书API的测试。为此,我们需要在package.json文件中添加一个测试脚本:

"scripts": {
    "test": "jest --testTimeout 20000"
},

在软件包文件的“脚本”部分中,我们添加了一个“测试”脚本,该脚本以20,000毫秒(20秒)的测试超时运行开玩笑。测试超时对于为API测试提供足够的时间完成至关重要,因为它们涉及与数据库的交互。

运行测试,打开终端并运行以下命令:

npm test

Jest将在server.test.js文件中执行测试用例,您应该在终端中查看测试结果。

Terminal interface showing the 5 passed test

欢呼。测试通过了。恭喜您编写您的第一个测试!

结论

总而言之,该初学者指南为您提供了一个可靠的起点,用于了解API测试以及如何使用Jest和Supertest测试Node.js API。为了进一步提高您的技能,我鼓励您探索在线可用的其他资源,包括Jest和Supertest的官方文档。此外,通过各种测试方案练习将有助于您在API测试方面获得信心和专业知识,使您成为当今软件开发环境中更熟练的开发人员。