大多数 RESTFUL API 函数无法进行单元测试
Most RESTFUL API functions cannot be unit-tested
postAdmin(req, res, cb) {
const admin = await Admin.findOne({ req.body.username });
if (!admin) {
error("Admin doesn't exist");
} //split here? adminValidationDAO();
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch){
error("Wrong password");
} //split here? passwordValidationDAO();
const email = new Email ({
text: req.body.text,
title: req.body.title,
});
const response = await email.save();
//split here? saveResponseDAO()?
}
Unit testing is the practice of testing small pieces of code,
typically individual functions, alone and isolated. If your test uses
some external resource, like the network or a database, it’s not a
unit test.
我正在考虑测试上面的代码,但在对单元测试进行了一些研究后我意识到我认为的单元测试是集成测试。测试 postAdmin 中的整个代码块将是集成测试。然后我想知道是否可以将postAdmin中的代码分成三块并使它们可单元测试,但存在以下问题:
第一个使用 findOne() 调用数据库。
第二个依赖库,那不是外部资源吗?
第三个使用 save() 进行数据库调用。
那么您是否同意没有任何东西可以进行单元测试?
您肯定想测试边缘情况,例如查询失败时抛出的错误或 return null。您应该尝试使用覆盖工具来帮助您确定单元测试覆盖了所有分支(路径)。 Istanbul 是一个很棒且常用的工具。通过这样做,您可以了解测试执行了哪些语句(您应该以 100% 为目标,尽管这有点不可能)。您可以使用模拟框架来模拟外部依赖项并驱动它们的行为,以便您可以专注于您的功能。
例如,我会将 Admin.findOne({req.body.username });
模拟为 return null,并测试是否抛出错误。
我会为 bcrypt.compare(password, user.password);
做同样的事情,并将其设置为 return false,这样我就可以测试是否抛出了错误。
这样做,您将拥有一套测试,让您可以重构功能,而不必担心改变行为。
So would you agree that there's nothing that can be unit tested?
否:我看到了逻辑和分支,以及新值的计算。所以你这里有可以单独测试的代码。但是您可能需要更改设计的其他部分才能实现这一点。
这是 TDD 的重要组成部分 - 用更容易测试的设计替换难以测试的设计。
在这里,您有三个函数(每个函数都很难测试)
admin = f(req.body.username)
isMatch = g(password, user.password)
response = h(req.body.text, req.body.title)
...然后是一堆逻辑,不关心这三个函数是怎么实现的,只关心返回什么值。因此,您可以使用您喜欢的这三种元素的任何替代品来测试其他所有内容。
postAdmin(req, res, cb, f, g, h) {
const admin = f({ req.body.username });
if (!admin) {
error("Admin doesn't exist");
} //split here? adminValidationDAO();
const isMatch = await g(password, user.password);
if(!isMatch){
error("Wrong password");
} //split here? passwordValidationDAO();
const email = new Email ({
text: req.body.text,
title: req.body.title,
});
const response = h({
text: req.body.text,
title: req.body.title,
});
//split here? saveResponseDAO()?
}
一旦有了这个想法,您就可以尝试不同的设计来管理依赖关系。
同样,由于逻辑与难以测试的部分分离,您可以将逻辑放在显微镜下并确保一切正常......即使面对使用"real" 这些依赖项的实现。
postAdmin(req, res, cb) {
const admin = await Admin.findOne({ req.body.username });
if (!admin) {
error("Admin doesn't exist");
} //split here? adminValidationDAO();
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch){
error("Wrong password");
} //split here? passwordValidationDAO();
const email = new Email ({
text: req.body.text,
title: req.body.title,
});
const response = await email.save();
//split here? saveResponseDAO()?
}
Unit testing is the practice of testing small pieces of code, typically individual functions, alone and isolated. If your test uses some external resource, like the network or a database, it’s not a unit test.
我正在考虑测试上面的代码,但在对单元测试进行了一些研究后我意识到我认为的单元测试是集成测试。测试 postAdmin 中的整个代码块将是集成测试。然后我想知道是否可以将postAdmin中的代码分成三块并使它们可单元测试,但存在以下问题:
第一个使用 findOne() 调用数据库。
第二个依赖库,那不是外部资源吗?
第三个使用 save() 进行数据库调用。
那么您是否同意没有任何东西可以进行单元测试?
您肯定想测试边缘情况,例如查询失败时抛出的错误或 return null。您应该尝试使用覆盖工具来帮助您确定单元测试覆盖了所有分支(路径)。 Istanbul 是一个很棒且常用的工具。通过这样做,您可以了解测试执行了哪些语句(您应该以 100% 为目标,尽管这有点不可能)。您可以使用模拟框架来模拟外部依赖项并驱动它们的行为,以便您可以专注于您的功能。
例如,我会将 Admin.findOne({req.body.username });
模拟为 return null,并测试是否抛出错误。
我会为 bcrypt.compare(password, user.password);
做同样的事情,并将其设置为 return false,这样我就可以测试是否抛出了错误。
这样做,您将拥有一套测试,让您可以重构功能,而不必担心改变行为。
So would you agree that there's nothing that can be unit tested?
否:我看到了逻辑和分支,以及新值的计算。所以你这里有可以单独测试的代码。但是您可能需要更改设计的其他部分才能实现这一点。
这是 TDD 的重要组成部分 - 用更容易测试的设计替换难以测试的设计。
在这里,您有三个函数(每个函数都很难测试)
admin = f(req.body.username)
isMatch = g(password, user.password)
response = h(req.body.text, req.body.title)
...然后是一堆逻辑,不关心这三个函数是怎么实现的,只关心返回什么值。因此,您可以使用您喜欢的这三种元素的任何替代品来测试其他所有内容。
postAdmin(req, res, cb, f, g, h) {
const admin = f({ req.body.username });
if (!admin) {
error("Admin doesn't exist");
} //split here? adminValidationDAO();
const isMatch = await g(password, user.password);
if(!isMatch){
error("Wrong password");
} //split here? passwordValidationDAO();
const email = new Email ({
text: req.body.text,
title: req.body.title,
});
const response = h({
text: req.body.text,
title: req.body.title,
});
//split here? saveResponseDAO()?
}
一旦有了这个想法,您就可以尝试不同的设计来管理依赖关系。
同样,由于逻辑与难以测试的部分分离,您可以将逻辑放在显微镜下并确保一切正常......即使面对使用"real" 这些依赖项的实现。