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()?
}
单元测试是测试一小段代码的实践,通常是单独的功能,单独的和孤立的。如果您的测试使用一些外部资源,例如网络或数据库,它不是单元测试。
我当时想测试上面的代码,但是在对单元测试进行了一些研究之后,我意识到我认为单元测试就是集成测试。在postAdmin内部测试整个代码块将是集成测试。然后我想知道是否可以将postAdmin内的代码分为三个块并使它们可测试,但这是以下问题:
第一个使用findOne()进行数据库调用。
第二个依赖库,这不是外部资源吗?
第三个使用save()进行数据库调用。
所以您同意没有可以进行单元测试的内容吗?
您肯定想测试一些极端情况,例如查询失败或返回null时引发的错误。您应该尝试使用覆盖率工具来帮助您确定单元测试涵盖了所有分支(路径)。一种很棒的常用工具是Istanbul。通过这样做,您可以了解测试将执行哪些语句(您应该以100%为目标,尽管这有点不可能)。您可以使用模拟框架模拟外部依赖关系并驱动它们的行为,以便您可以专注于功能。
例如,我将模拟Admin.findOne({req.body.username });
以返回null,并测试是否引发了错误。
我将对bcrypt.compare(password, user.password);
执行相同的操作,并使其返回false,以便我可以测试是否引发了错误。这样做,您将拥有一整套测试,使您可以重构功能而不必担心改变行为。
所以您同意没有可以进行单元测试的内容吗?
否:我看到了逻辑和分支,以及新值的计算。因此,这里有可以单独测试的代码。但是您可能需要更改设计的其他部分才能实现此目的。
这是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()?
}
一旦有了这个想法,就可以尝试不同的设计来管理依赖关系。
再次,因为逻辑与难以测试的部分分离,您可以将逻辑放在显微镜下,并确保一切正常……即使使用“真实”时难以再现的响应这些依赖项的实现。