谷歌浏览器bug调试快捷键

As web developers, it often feels like we spend more time fixing bugs and trying to solve problems than we do writing code. In this guide we'll look at some common debugging techniques, so let's get stuck in.

作为Web开发人员,通常感觉我们比编写代码花费更多的时间来修复错误和尝试解决问题。 在本指南中,我们将介绍一些常见的调试技术,因此请紧紧抓住它。

“准备失败,准备失败” ("Fail to prepare, prepare to fail")

What better way to start an article than with an old cliche! Bug's and issues are going to pop up. There is simply no way of getting away from this (sorry). With some simple planning, there are ways in which we can minimize the complexity and number of problems we face.

与旧的陈词滥调相比,还有什么更好的开始文章的方式! 错误和问题将会弹出。 根本没有办法摆脱这个(抱歉)。 通过一些简单的计划,我们可以通过多种方法所面临问题的复杂性和数量降至最低

将任务分解为较小的任务 (Break the task up into smaller tasks)

Now, I get it, we all like to get very excited and dive straight into our coding projects. The thing is, without some sort of plan we are creating problems for ourselves before we even start!

现在,我明白了,我们大家都非常兴奋,直接进入我们的编码项目。 问题是,如果没有某种计划,我们甚至在开始之前就为自己制造问题!

If I said to you, "you have to build a shopping list app", and you started coding straight away, what would happen? You would end up staring at a blinking cursor wondering how or what to do first, cursing my name under your breath for asking you to do such a task.

如果我对您说:“您必须构建一个购物清单应用程序”,并且立即开始进行编码,那会发生什么? 您最终将盯着一个闪烁的光标,想知道该怎么做或先做什么,然后屏住呼吸诅咒我的名字,要求您执行此任务。

It's always easier to take a large problem and break it up into many smaller problems. For example, we can break the shopping list project into smaller tasks:

将大问题分解成许多小问题总是比较容易。 例如,我们可以将购物清单项目分解为较小的任务:

  • Create a form to add an item to the list创建一个表单以将项目添加到列表
  • Allow a user to remove an item from the list允许用户从列表中删除项目
  • Display a total number of items in the list显示列表中的项目总数

You can even break these tasks up into more detailed tasks. For example, for the first task in our list, our first, "little mini task" (should I trademark that term?) could be:

您甚至可以将这些任务分解为更详细的任务。 例如,对于列表中的第一个任务,我们的第一个“小微型任务”(我应该用这个商标吗?)可以是:

1) Create an input field to capture the item name

1)创建一个输入字段以捕获项目名称

2) Create a button that calls a functions addToList() when clicked

2)创建一个按钮,当单击该按钮时调用函数addToList()

3) Write logic within the addToList() function that adds the item to the list

3)在将项目添加到列表的addToList()函数内编写逻辑

And so on. You get the idea. I prefer breaking work up like this as it really makes me think about the problems I'll encounter early and the solutions (I've written an in-depth article about this here) before I've written any code. It also helps me understand what I'm trying to do, and puts me in the "zone". It's much easier to solve problems that arise when you understand what you are trying to achieve.

等等。 你明白了。 我更喜欢像这样分手工作,因为这确实使我在写任何代码之前考虑一下我会较早遇到的问题和解决方案( 我在这里写了一篇深入的文章 )。 它还可以帮助我了解我要执行的操作,并将我置于“区域”。 解决了当您了解要实现的目标时出现的问题要容易得多。

准备清除您的代码 (Be prepared to purge your code)

To make an omelet, we have to break a few eggs. This means being prepared to completely re-write our code to get it working.

要制作煎蛋卷,我们必须打碎几个鸡蛋。 这意味着准备完全重新编写我们的代码以使其工作。

I bet you're thinking, "oh man, it took me days/weeks/millennia to get this far with my code, and now I have to delete it?!"  Um, yeah. Sometimes. Sorry. The reality with web development is that code will change all the time, for various reasons - bugs, code reviews, changes in requirements, boredom, etc.

我敢打赌,您在想,“哦,伙计,花了我几天,几周,几千年的时间,我的代码才能做到这一点,现在我必须删除它了!!” 嗯是的 有时。 抱歉。 Web开发的现实是,由于各种原因,代码将一直在变化-错误,代码审查,需求变化,无聊等。

Sometimes we feel so precious about our code and can't bear to delete it, that we try and overcome problems by trying to "fit a round peg into a square hole". We think "NOO! I can't possibly delete this method. It took me forever. There has got to be a way!" This mental block causes our own problems - because by simply rewriting what we currently have, we could find the solution to our problems.

有时我们对代码感到如此珍贵,不愿删除它,以至于我们试图通过“将圆钉固定在方Kong中”来克服问题。 我们认为“没有!我无法删除此方法。这花了我一辈子。一定有办法!” 这种思维障碍导致了我们自己的问题-因为只需重写当前的内容,我们就能找到解决问题的方法。

Now that we're nice and prepared, let's look at what happens when things do go wrong.

现在我们已经准备好了,让我们看看当事情出错时会发生什么。

错误消息很好 (Error messages are good)

What's worse than seeing an error message when something goes wrong? Not seeing any error message when something goes wrong. Even though it's a daunting feeling to see a big red stack trace when we run our carefully crafted code, error messages are there to say "yeah, things are messed up right now, but here are some places you can look to start fixing it".

有什么比发生错误时看到错误消息更糟糕的呢? 出现问题时看不到任何错误消息 。 尽管在运行我们精心设计的代码时看到巨大的红色堆栈痕迹令人生畏,但错误消息仍在上面说:“是的,事情现在已经搞砸了,但是您可以在这里找到一些地方开始修复它” 。

If we have a look at this example:

如果我们看一下这个例子:

let animals;function addAnimal(){animals.push('elephant');
}addAnimal();

Now let's have a look at the error:

现在让我们看一下错误:

I've left out some of the stack trace as most of it is, well, gibberish. Depending on how your frontend project handles error messages, you may even see the error in your browser:

我省去了一些堆栈跟踪信息,因为大部分都是乱码。 根据前端项目处理错误消息的方式,您甚至可能在浏览器中看到错误:

The important parts of the stack trace are usually at the top - the message, the function, and the line number, and our browser shows us this as well. So the interpreter does it's best to tell us what's wrong. It's a shame it can't solve the problem for us, eh?

堆栈跟踪的重要部分通常位于顶部-消息,函数和行号,我们的浏览器也向我们显示了这一点。 因此,口译员最好告诉我们出了什么问题。 可惜不能为我们解决问题,是吗?

So, we've finished having our panic attack at seeing the error and have picked some information from the error message. If we break it down we can see this line:

因此,我们已经对发现错误进行了紧急恐慌,并从错误消息中选择了一些信息。 如果我们将其分解,可以看到以下行:

Cannot read property 'push' of undefined

This usually means that there is a variable not defined or initialized somewhere. BUT WHERE?!?

这通常意味着某个变量未在某处定义或初始化。 但是哪里?!?

If we continue reading the stack trace, we see the error occurs within the addAnimal() function. We can see that we are trying to push a new animal to an array - ah! I forgot to initialize the animals array. Doh! Our fixed code looks like this:

如果我们继续读取堆栈跟踪,我们会在addAnimal()函数中看到错误发生。 我们可以看到我们正在尝试将新动物推入阵列-啊! 我忘了初始化animals数组。 h! 我们的固定代码如下所示:

let animals = [];function addAnimal() {animals.push("elephant");
}addAnimal();

The error thrown in the browser will show you the problem quicker, but not all frontend projects will be configured to do this, and backend developers do not have this luxury.  This is why I recommend learning to read the stack trace.

浏览器中引发的错误将更快地向您显示问题,但并非所有前端项目都将配置为执行此操作,并且后端开发人员没有这种奢望。 这就是为什么我建议学习阅读堆栈跟踪的原因。

要击败该错误,您必须像错误一样思考 (To defeat the bug, you must think like the bug)

The stack trace gives you an idea of what the error is. Well, sometimes it does and sometimes it doesn't. What if you see an error message that looks more like cave glyphs than English? Or what if there is no error, but your code is simply not acting as you thought?

堆栈跟踪使您可以了解错误的原因。 好吧,有时候会,有时候不会。 如果您看到一条错误消息,看起来比英语更像洞穴字形怎么办? 或者,如果没有错误,但是您的代码根本没有按照您的想法工作,该怎么办?

It's time to get the debugger out. Let's walk through another example. But first some context!

是时候将调试器发布了。 让我们来看另一个例子。 但是首先要有上下文!

Mr. Bob CEO (who is a CEO, who would have thought?!) approaches you and says,

鲍勃(Bob)首席执行官(谁是首席执行官,谁会想到的!

"Hey, I have an amazing idea for a product.

“嘿,我对产品有一个很棒的主意。

  • I want a web app that allows the user to enter a number.我想要一个允许用户输入数字的Web应用程序。
  • If the number is less than 5, the message should read "UNDER".如果数字小于5,则消息应显示为“ UNDER”。
  • If the number is equal to or more than 5, the message should read "OVER".如果数字等于或大于5,则消息应显示为“ OVER”。

This is a million-dollar idea and I want you to build it for me".

这是一个数百万美元的想法,我希望您为我建立它。”

"OKAY!" You say, and you get to work.

“好的!” 你说,就可以上班了。

*Coding montage with dramatic music plays as time fast forwards*

*随着时间的推移,将蒙太奇与戏剧性音乐一起播放*

You've completed the code for your web app. Huzzah!

您已经完成了Web应用程序的代码。 晕!

(Note: You may have spotted the bug already. If you did, let's pretend you didn't. If you haven't noticed the bug, that's OK.)

(注意:您可能已经发现了该错误。如果发现了,请假装您没有。如果您没有注意到该错误,那没关系。)

Time to start testing. Let's run through some use cases for our business logic.

是时候开始测试了。 让我们来看一些业务逻辑用例。

1) User enters 3:

1)用户输入3:

2) User enters 7

2)用户输入7

So far so good! But what happens if we enter 5?

到目前为止,一切都很好! 但是,如果我们输入5会怎样?

OH NO! A bug! The text displayed is incorrect, it should display "OVER" but instead displays "UNDER". Hmm, no error messages, and I can't seem to see in the code what is wrong. Let's run the debugger and step through the code.

不好了! 虫子! 显示的文本不正确,应显示“ OVER ”,而显示“ UNDDER ”。 嗯,没有错误消息, 而且我似乎看不出代码有什么问题。 让我们运行调试器并逐步执行代码。

使用调试器 (Using the debugger)

A good place to start is to put a breakpoint as close to the buggy code as possible. You can determine this by reading the code, error messages, or if you have that "ah-ha! I know which method is causing this" moment. From here, it's a case of stepping through the code, inspecting the variables, and checking if the correct code branches are run.

一个不错的起点是将断点放置在尽可能靠近越野车代码的位置。 您可以通过阅读代码,错误消息或是否有“ 啊哈! 我知道是哪种方法导致了这一时刻” 来确定这一点。 从这里开始,需要逐步执行代码,检查变量并检查是否运行了正确的代码分支。

In our example, I have put a breakpoint at the start of my if statement - as this is where the failing logic is.

在我们的示例中,我在if statement的开头放置了一个断点-因为这是逻辑失败的地方。

Once I start debugging, chrome opens and I can replicate the issue by entering "5" and clicking submit. This hits the breakpoint, and immediately there are a few things to note:

开始调试后,Chrome打开,可以通过输入“ 5”并单击“提交”来复制问题。 这到达了断点,立即有几件事要注意:

  • The debugger stops at the breakpoint, so this means I'm on the right track调试器在断点处停止,所以这意味着我在正确的轨道上
  • This also means that the function is being called correctly, and event handlers are working as expected这也意味着该函数被正确调用,并且事件处理程序正在按预期方式工作
  • I can also see that the user input is being captured correctly (from the "variables" panel on the left-hand side, I can see that "5" was entered)我还可以看到用户输入已被正确捕获(从左侧的“变量”面板中,我看到输入了“ 5”)

So far so good, no immediate issues to worry about. Well, code related ones anyway. Next, I'll press F10 to step through the code. This runs each statement individually, allowing us to inspect elements, variables, and other things at our own pace. Isn't technology just fabulous?

到目前为止,一切都很好,无需担心任何即时问题。 好吧,还是与代码相关的代码。 接下来,我将按F10键单步执行代码 。 这将单独运行每个语句,使我们能够按照自己的步调检查元素,变量和其他事物。 技术不只是神话般的吗?

Remember since I expect the message "OVER" to appear when the user enters "5", I'm expecting the debugger to take me into the first branch of the if statement...

请记住,由于我希望在用户输入“ 5”时出现消息“ OVER”,所以我希望调试器将我带入if语句的第一个分支...

BUT NO! I'm brought into the second branch. Why? I need to amend the conditional to read "more than or equals to" as opposed to "more than".

但不是! 我被带到第二个分支。 为什么? 我需要修改条件,以将“ 大于或等于”读为“大于”。

if(numberInputValue >= 5) {text = "OVER";
} else {text = "UNDER";
}

Success! Our bug is fixed. Hopefully this gives you an idea on how to walk through the code, making use of VSCodes awesome debugging tools.

成功! 我们的错误已修复。 希望这可以使您了解如何使用VSCodes出色的调试工具来遍历代码。

更多调试技巧 (More Debugging tips)

  • If your breakpoints aren't being hit, this could be part of the issue. Make sure the correct functions or event handlers are being called in the way you expect如果没有遇到断点,这可能是问题的一部分。 确保以您期望的方式调用正确的函数或事件处理程序
  • You can step over functions you want to skip. If you want to debug any functions you come across, use the step into command

    你可以step over要跳过功能。 如果要调试遇到的任何功能,请使用step into命令

  • Watch out for variables, parameters, and arguments as you are stepping through your code. Are the values what you expect?在逐步执行代码时,请注意变量,参数和参数。 这些值是您期望的吗?
  • Write code in a way that is easier to read/debug. It might look cool to have your code on one line, but it makes debugging harder以更易于阅读/调试的方式编写代码。 将代码放在一行中可能看起来很酷,但是这会使调试更加困难

Google是你的朋友 (Google is your friend)

Ok so we've looked at the stack-trace, tried debugging, and we're still stuck with this bug. The only thing left to do now is make a sacrifice to the coding gods and hope things fix themselves!

好的,我们已经查看了堆栈跟踪,尝试了调试,但仍然遇到此错误。 现在剩下要做的唯一一件事就是牺牲编码神,并希望事情能够解决!

Or I guess we could use Google.

或者我想我们可以使用Google。

Google is a treasure trove of software development problems and solutions, all at our fingertips. It can be sneakily difficult to access this information though, as you have to know how to Google the right things to get the right information! So how do we effectively use Google?

Google唾手可得,是软件开发问题和解决方案的宝库。 但是,由于您必须知道如何使用Google的正确方法来获取正确的信息,因此很难获得这些信息! 那么,我们如何有效地使用Google?

Let's look back to our first example - you've read the stack trace, looked at the code, but the message Cannot read property 'push' of undefined is still driving you mad. Bewildered, you take to Google in hopes of finding an answer. Here are some things to try:

让我们回头看第一个示例-您已经阅读了堆栈跟踪,查看了代码,但是消息Cannot read property 'push' of undefined仍然使您发狂。 迷惑不解,您带去Google希望找到答案。 这里有一些尝试:

Copy and paste the error message. Sometimes this works, depending on how "generic" the error message is. For example, if you get a Null pointer exception (who doesn't love those?), Googling "Null pointer exception" might not return very helpful results.

复制并粘贴错误消息。 有时这可以起作用,具体取决于错误消息的“通用”程度。 例如,如果您得到一个Null指针异常 (谁不喜欢那些异常 ?),则搜索“ Null指针异常”可能不会返回非常有用的结果。

Search for what you are trying to do. For example, "How to create an array and add an item to the end". You might find some generous developer has posted a solution using best practices on StackOverflow, for example. You might also find this solution is completely different from yours - remember what I said about being comfortable purging your code?

搜索您要执行的操作。 例如, “如何创建数组并在最后添加项目” 。 例如,您可能会发现一些慷慨的开发人员已在StackOverflow上发布了使用最佳实践的解决方案。 您可能还会发现此解决方案与您的解决方案完全不同-还记得我说过的关于轻松清除代码的说法吗?

A side note on using someone else's code - try and avoid blindly copying and pasting, make sure you understand what the code does first!

关于使用他人代码的附加说明-尝试避免盲目复制和粘贴,请确保您首先了解代码的作用!

以正确的方式寻求帮助 (Ask for help the right way)

Hopefully, after a mix of debugging, stack trace investigating, and Googling you have seen the light at the end of the tunnel and solved your problem. Unfortunately, depending on what you are trying to do, you still might be a bit stumped. This is a good time to seek advice from other people.

希望在经过调试,堆栈跟踪调查和Googling混合之后,您已经看到了隧道尽头的曙光,并解决了您的问题。 不幸的是,根据您要尝试执行的操作,您仍然可能会有些困惑。 这是寻求他人意见的好时机。

Now, before you run into the street screaming "my code is broken please help me!", it's important to know the best way to ask for help. Asking for help in the right way makes it easier for people to understand the problem and help you solve it. Let's look at some examples:

现在,在街上大喊“我的代码已损坏,请帮助我!”之前,了解寻求帮助的最佳方法很重要。 以正确的方式寻求帮助可以使人们更容易理解问题并帮助您解决问题。 让我们看一些例子:

Bad - "My code is broken and I don't know what's wrong."

错误 -“我的代码已损坏,我不知道出了什么问题。”

Good - "I'm trying to add an item to the end of an array in JavaScript, and I'm getting this error message: Cannot read property 'push' of undefined. Here's my code so far."

-“我正在尝试在JavaScript中将项目添加到数组的末尾,并且收到此错误消息:无法读取未定义的属性'push'。到目前为止,这是我的代码。”

See how the "Good" example is much more informative? More information makes it easier for other kindhearted devs to help you out. This is a good habit to get into as it not only benefits you when you are learning to code but also in your first job when you need to ask for help.

看看“好”示例如何提供更多信息? 更多信息使其他善良的开发人员可以更轻松地为您提供帮助。 这是一个良好的习惯,因为它不仅在您学习编程时对您有所帮助,而且在您需要寻求帮助时会在您的第一份工作中受益。

So where can you ask for help?

那么,您可以在哪里寻求帮助?

  • StackOverflow堆栈溢出
  • Twitter推特
  • Slack groups松弛组
  • Colleagues or developer friends同事或开发者朋友

Quick Tip: You can use a tool like CodeSandbox.io or CodePen.io to recreate your problem and share it with people.

快速提示:您可以使用CodeSandbox.io或CodePen.io之类的工具来重新创建问题并与他人共享。

练习练习 (Practice, practice, practice)

Just like Rome wasn't built in a day (well, it could have been for all I know) you will not become king bug squasher overnight. As your career goes on and your experience grows, you'll be armed with a wealth of knowledge that helps you solve bugs and issues faster. I regularly find myself saying "ah, I've solved that before" or "oh yeah, I have a StackOverflow link that helps me here" and the whole thing becomes a lot easier. So keep practicing, and this skill will grow naturally.

就像罗马不是一天建成的(好吧,据我所知),您不会在一夜之间成为臭虫抢劫者。 随着您事业的发展和经验的增长,您将获得大量知识,可以帮助您更快地解决错误和问题。 我经常发现自己在说“啊,我之前已经解决了这个问题”或“哦,是的,我有一个StackOverflow链接可以在这里帮助我”,整个过程变得容易得多。 因此,继续练习,该技能将自然增长。

Thanks for reading! If you enjoyed this article, why not subscribe to my newsletter?

谢谢阅读! 如果您喜欢这篇文章, 为什么不订阅我的新闻通讯呢?

Every week I send out a list of 10 things I think are worth sharing — my latest articles, tutorials, tips and interesting links for upcoming developers like you. I also give out some free stuff from time to time :)

每周,我都会发送一份我认为值得分享的10件事清单 -我的最新文章,教程,技巧和有趣的链接,为像您这样的即将来临的开发人员提供。 我也会不时提供一些免费的东西:)

翻译自: https://www.freecodecamp.org/news/the-beginner-bug-squashing-guide/

谷歌浏览器bug调试快捷键

谷歌浏览器bug调试快捷键_Bug压榨初学者指南:如何使用调试器和其他工具查找和修复Bug相关推荐

  1. 谷歌最新开源的工具可以自动化查找并修复 bug!

    作者 | h4cd 本文经授权转自开源中国 近日,谷歌开源了一个模糊测试基础设施--ClusterFuzz,可以非常简单地自动化查找并修复程序中的 bug. 模糊测试是一种用于自动化检测软件中存在的问 ...

  2. 谷歌开源 ClusterFuzz,自动化查找并修复 bug

    开发四年只会写业务代码,分布式高并发都不会还做程序员? >>>   近日,谷歌开源了一个模糊测试基础设施--ClusterFuzz,可以非常简单地自动化查找并修复程序中的 bug. ...

  3. 【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 使用 addr2line 命令行工具查找动态库中的报错代码位置 )

    文章目录 一.从 Tombstone 报错日志中查找报错动态库 二.addr2line 命令行工具使用 64 位动态库使用的 aarch64-linux-android-addr2line.exe 工 ...

  4. vs debug 调试 快捷键

    F1:在线帮助 Ctrl+F:查找与替换 F3: 查找下一个 Shift+F3: 查找上一个 F4:属性 Ctrl+F4:关闭当前窗体 F6: 生成解决方案 Ctrl+F6: 生成当前项目 F7: 查 ...

  5. 修复bug的12个关键步骤

    要多少时间才能修复bug,事先是很难知道的,特别是如果你和这些代码还素不相识的话,情况就更加扑朔迷离了.James Shore在<The Art of Agile >一书中,明确指出要想修 ...

  6. “缺陷管理工具”禅道—升华Bug处理流程与相关属性

    "缺陷管理工具"禅道-升华Bug处理流程与相关属性 作为一个软件测试工程师,对缺陷管理工具(缺陷:Bug)的认识和准确操作是有所必要的,缺陷管理工具现在行业中有很多:禅道.QC.C ...

  7. 还在肉眼找bug??赶紧进来!!!程序员一定要学的调试技巧.

    本文介绍了什么是bug,什么是调试,调试重要性,如何调试解决bug,各种常用的调试快捷键,如何写出好代码以及const关键字,assert断言库函数介绍,写代码各种遇见的错误, 调试训练 实用调试技巧 ...

  8. python编程初学者指南pdf-Python物理建模初学者指南

    Python物理建模初学者指南 下载 mobi epub pdf ☆☆☆☆☆ [美] Jesse,M.Kinder,Philip,Nelson 著,盖磊 译 下载链接在页面底部 发表于2020-10- ...

  9. 《C语言编程初学者指南》一1.9 本章小结

    本节书摘来自异步社区<C语言编程初学者指南>一书中的第1章,第1.9节,作者[美]Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云 ...

最新文章

  1. JSP内置对象-out
  2. 持续交付流水线的敏捷利器:环境配置管理与应用部署自动化
  3. django使用ckeditor富文本编辑器-转
  4. HDU4970 Killing Monsters dp
  5. Angular、Vue、React 和前端的未来
  6. java B2B2C springmvc mybatis电子商城系统(四)Ribbon
  7. 手机调试python的软件_Appium+Python(ios真机移动端App H5混合自动化实战测试)
  8. [TimLinux] scrapy 在Windows平台的安装
  9. CodeForces615A-Bulbs-模拟
  10. python利用字典破解WIFI密码
  11. [语音处理] 声谱图(spectrogram)FBank(Mel_spectrogram)MFCC(Mel倒谱)到底用哪个作为NN输入?
  12. 数据库事物,隔离级别慢慢深入
  13. 开通了个人微信公众号:slbGTD,准备把GTD相关的内容写成一本书
  14. 云存储及其分布式文件系统
  15. Android实现登录邮箱的自动补全功能
  16. 在编程中常见的一些英语词汇
  17. 面试之前,简历之上:给前端校招同学的简历建议
  18. 初识MFC----MFC简介
  19. 离线安装PostgreSQL数据库(v13.4版本)
  20. RetinaNet论文详解Focal Loss for Dense Object Detection

热门文章

  1. fork、vfork、clone
  2. java学习笔记11 (构造方法 this深探)
  3. 测试工具之badboy
  4. 用Docker自动构建纸壳CMS
  5. 读zepto核心源码学习JS笔记(3)--zepto.init()
  6. sqlserver游标概念与实例全面解说
  7. 【练习5.9】图像掩码、礼帽、cvCopy、图像融合、cvCvtColor
  8. 【深度学习系列】用PaddlePaddle和Tensorflow实现经典CNN网络AlexNet
  9. Exchange server 2010系列教程之三 发送邮件测试
  10. Python操作MongoDB - 极简教程