我正在尝试为一种方法编写单元测试,但在使事情顺利运行方面遇到一些麻烦。
我需要测试此类中唯一的public
方法AuthenticateAdminService.authAdmin
,该方法将调用private
方法,该方法将调用jsonwebtoken
等第三方库和bcrypt
。我遇到的问题是,当我尝试将密码与bcrypt
进行比较时,它在自动测试过程中返回为false
,而在手动测试过程中返回了true
。
我应该如何可靠地模拟这些私有方法和库?我在SO上找到了几篇对我无济于事的文章,也许我只是听不懂答案。我发现this post on mocking libraries确实起到了作用,直到我意识到我正在使用的任何库都包装在一个私有方法内,这对我的测试没有好处,因为我没有直接调用模拟库。>
我认为我应该创建AuthenticateAdminService
类私有方法的模拟。测试实际的公共方法authAdmin
,并以某种方式获取被调用的模拟私有方法,而不是实际版本。有帮助吗?
class AuthenticateAdminService {
public static async authAdmin(reqBody: RequestBody, AdminModel: AdminModel): Promise<object> {
const { username, password } = reqBody
const adminRow: AdminRow = await AdminModel.findOne({ where: { username } })
let token: { token?: string } = {}
if (adminRow !== null) {
token = await AuthenticateAdminService.checkIfPasswordsMatch(password, adminRow)
}
return token
}
private static async checkIfPasswordsMatch(plainPassword: string, adminRow: AdminRow): Promise<object> {
const isPasswordsMatch = await Promise.resolve(bcrypt.compare(plainPassword, adminRow.password))
let token: object = {}
if (isPasswordsMatch) {
const admin: object = AuthenticateAdminService.removePasswordPropFromAdminRow(adminRow)
token = { token: await AuthenticateAdminService.createToken(admin) }
}
return token
}
private static removePasswordPropFromAdminRow(adminRow: AdminRow): object {
const { password, ...admin } = adminRow.dataValues
return admin
}
private static async createToken(admin: object): Promise<string> {
const token: string = await Promise.resolve(jwt.sign({ admin }, 'expressadminarea'))
return token
}
}
import { AuthenticateAdminService } from '../../src/services/AuthenticateAdminService/AuthenticateAdminService'
type AdminModel = {
findOne(where: object): AdminRow
}
type AdminRow = {
password: string
dataValues: { password: string }
}
test('authAdmin', async () => {
const reqBody: { username: string, password: string } = { username: 'foo', password: 'foo' }
const adminModel: AdminModel = { findOne: (_where) => <AdminRow>{ password: 'foo', dataValues: { password: 'foo' } } }
const token: { token?: string } = await AuthenticateAdminService.authAdmin(reqBody, adminModel)
expect(typeof token).toBe('object')
expect(typeof token.token).toBe('string') // token.token is undefined
expect(token.token.length).toBeGreaterThan(0)
})
答案 0 :(得分:1)
看起来您只需要用密码哈希而不是纯文本密码来模拟adminModel
:
test('authAdmin', async () => {
const reqBody: { username: string, password: string } = { username: 'foo', password: 'foo' }
const hash = await bcrypt.hash('foo', 10); // create a hash
const adminModel: AdminModel = { findOne: (_where) => <AdminRow>{ password: hash, dataValues: { password: hash } } } // use the hash
const token: { token?: string } = await AuthenticateAdminService.authAdmin(reqBody, adminModel)
expect(typeof token).toBe('object')
expect(typeof token.token).toBe('string') // Success!
expect(token.token.length).toBeGreaterThan(0)
})
此外,bcrypt.compare
返回一个Promise
,因此您可以简化该行:
const isPasswordsMatch = await bcrypt.compare(plainPassword, adminRow.password);