与AdonisJs的TDD价格 – 4.使用auth中间件

我们的路线目前可以由未经过身份验证的用户访问,所以让我们编写一个新的测试来确认这一点

与往常一样,您可以在GitHub上的以下提交中找到我们所做的所有更改:https://github.com/MZanggl/tdd-adonisjs/commit/6f50e5f277674dfe460b692cedc28d5a67d1cc55

// test/functional/thread.spec.js  test('unauthenticated user cannot create threads', async ({ client }) => {   const response = await client.post('/threads').send({     title: 'test title',     body: 'body',   }).end()    response.assertStatus(401) }) 

测试失败,因为响应代码仍然是200.所以让我们将集成的auth中间件添加到我们的路由中。

// start/routes.js  Route.resource('threads', 'ThreadController').only(('store', 'destroy')).middleware('auth') 

这使测试通过,但同时,我们打破了其他测试,因为他们现在也返回状态代码401(未经验证)。
为了使它们再次通过,我们需要能够在测试中与用户进行身份验证。

首先,让我们为用户创建一个模型工厂,就像我们使用线程一样。

回头去 database/factory.js 并为用户添加以下蓝图。

Factory.blueprint('App/Models/User', (faker) => {   return {     username: faker.username(),     email: faker.email(),     password: '123456',   } }) 

让我们在我们的函数thread.spec.js测试中尝试一下我们可以使用“登录” loginVia 方法。

test('can create threads', async ({ client }) => {   const user = await Factory.model('App/Models/User').create()   const response = await client.post('/threads').loginVia(user).send({     title: 'test title',     body: 'body',   }).end()    response.assertStatus(200)    const thread = await Thread.firstOrFail()   response.assertJSON({ thread: thread.toJSON() }) }) 

但是,这会因错误而失败 ...loginVia is not a function。像以前一样,特质可以帮助我们解决这个问题,所以让我们补充一下 trait('Auth/Client') 到文件的顶部并再次运行测试。

甜让我们对现有的失败删除测试应用相同的修复。

test('can delete threads', async ({ assert, client }) => {   const user = await Factory.model('App/Models/User').create()   const thread = await Factory.model('App/Models/Thread').create()   const response = await client.delete(thread.url()).send().loginVia(user).end()   response.assertStatus(204)    assert.equal(await Thread.getCount(), 0) }) 

当然,任何用户都可以删除任何线程并不是最佳的,但我们正在实现这一目标……

我认为现在是时候将测试用例重命名为更有意义的东西了。

test('可以创建线程')=> test('授权用户可以创建线程')

test('可以删除线程')=> test('授权用户可以删除线程')

完成此操作后,将user_id列添加到线程表是有意义的。

为此,我们首先必须重构我们的测试用例'授权用户可以创建线程'。我们目前没有测试标题和正文是否正确插入,我们只是断言响应与数据库中找到的第一个线程相匹配。所以我们也要添加这个部分

test('authorized user can create threads', async ({ client }) => {   const user = await Factory.model('App/Models/User').create()   const attributes = {     title: 'test title',     body: 'body',   }    const response = await client.post('/threads').loginVia(user).send(attributes).end()   response.assertStatus(200)    const thread = await Thread.firstOrFail()   response.assertJSON({ thread: thread.toJSON() })   response.assertJSONSubset({ thread: attributes }) }) 

测试仍然应该通过,但是让我们继续将user_id添加到我们添加的断言中

response.assertJSONSubset({ thread: {...attributes, user_id: user.id} }) 

我们现在收到错误

expected { Object (thread) } to contain subset { Object (thread) }   {     thread: {     - created_at: "2019-09-08 08:57:59"     - id: 1     - updated_at: "2019-09-08 08:57:59"     + user_id: 1     } 

所以让我们转到ThreadController并用这个替换“store”方法

async store({ request, auth, response }) {     const attributes = { ...request.only(('title', 'body')), user_id: auth.user.id }     const thread = await Thread.create(attributes)     return response.json({ thread })     } 

不用担心,我们会在测试结果为绿色后重构。

现在测试将在断言时失败 response.assertStatus(200) 有500个错误代码,所以让我们添加 console.log(response.error) 在上一行。它将揭示我们的表缺少列 user_id

转到线程迁移文件并在正文之后,像这样添加user_id列

table.integer('user_id').unsigned().notNullable() 

我们还用外键注册新列。我想在所有列声明后保留外键。

// ... column declarations  table.foreign('user_id').references('id').inTable('users') 

太棒了,这个测试又过去了

但事实证明我们打破了另外两个测试

我们的单元测试“可以访问url”,功能测试“授权用户可以删除线程”现在因为失败而失败 SQLITE_CONSTRAINT: NOT NULL constraint failed: threads.user_id

这两个测试都使用我们的模型工厂来处理线程,当然我们还没有使用用户ID更新它。所以让我们来看看吧 database/factory.js 并将user_id添加到线程工厂,如下所示:

return {     title: faker.word(),     body: faker.paragraph(),     user_id: (await Factory.model('App/Models/User').create()).id   } 

务必将功能转换为 async 功能,因为我们必须在这里使用等待。

如果我们再次运行我们的测试套件,我们应该变绿

重构

让我们转到ThreadController并想一下这个部分更面向对象的方法:

const attributes = { ...request.only(('title', 'body')), user_id: auth.user.id } const thread = await Thread.create(attributes) 

如果我们不必自己定义关系,那就太好了。
我们可以用这个换掉这两行

const thread = await auth.user.threads().create(request.only(('title', 'body'))) 

由于我们还没有定义关系,我们将得到错误 TypeError: auth.user.threads is not a function

所以我们要做的就是转到“App / Models / User.js”并添加关系

threads() {     return this.hasMany('App/Models/Thread') } 

就是这样,一个坚实的重构

让我们快速添加另一个测试,以确保未经身份验证的用户无法删除线程

test('unauthenticated user can not delete threads', async ({ assert, client }) => {   const thread = await Factory.model('App/Models/Thread').create()   const response = await client.delete(thread.url()).send().end()   response.assertStatus(401) }) 

当然,我们必须在这里添加更多测试,并非每个用户都应该能够简单地删除任何线程。下一次,让我们测试并创建一个为我们解决这个问题的策略

资讯来源:由0x资讯编译自DEV,原文:https://dev.to/mzanggl/tdd-course-with-adonisjs-4-using-the-auth-middleware-2kdi ,版权归作者所有,未经许可,不得转载
你可能还喜欢