diff --git a/docs/print.html b/docs/print.html
index bf98b58..76d5081 100644
--- a/docs/print.html
+++ b/docs/print.html
@@ -2,7 +2,7 @@
@@ -6038,6 +6038,136 @@ mod tests {
因为测试也不过是 Rust 代码而tests
也只是另一个模块,我们完全可以在一个测试中导入并调用internal_adder
。如果你并不认为私有函数应该被测试,Rust 也不会强迫你这么做。
+
在 Rust 中,集成测试对于需要测试的库来说是完全独立。他们同其他代码一样使用库文件。他们的目的是测试库的个个部分结合起来能否正常工作。每个能正确运行的代码单元集成在一起也可能会出现问题,所以集成测试的覆盖率也是很重要的。
+
+
Cargo 支持位于 tests 目录中的集成测试。如果创建它并放入 Rust 源文件,Cargo 会将每一个文件当作单独的 crate 来编译。让我们试一试!
+
首先,在项目根目录创建一个 tests 目录,挨着 src 目录。接着新建一个文件 tests/integration_test.rs,并写入列表 11-7 中的代码:
+
+
在开头使用了extern crate adder
,单元测试中并不需要它。tests
目录中的每一个测试文件都是完全独立的 crate,所以需要在每个文件中导入我们的库。这也就是为何tests
是编写集成测试的绝佳场所:他们像任何其他用户那样,需要将库导入 crate 并只能使用公有 API。
+
这个文件中也不需要tests
模块。除非运行测试否则整个文件夹都不会被编译,所以无需将任何部分标记为#[cfg(test)]
。另外每个测试文件都被隔离进其自己的 crate 中,无需进一步隔离测试代码。
+
让我们运行集成测试,同样使用cargo test
来运行:
+
$ cargo test
+ Compiling adder v0.1.0 (file:///projects/adder)
+ Running target/debug/deps/adder-abcabcabc
+
+running 1 test
+test tests::it_works ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/debug/integration_test-952a27e0126bb565
+
+running 1 test
+test it_adds_two ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Doc-tests adder
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+
+
现在有了三个部分的输出:单元测试、集成测试和文档测试。注意当在任何 src 目录的文件中增加单元测试时,单元测试部分的对应输出也会增加。增加集成测试文件中的测试函数也会对应增加输出。如果在 tests 目录中增加集成测试文件,则会增加更多集成测试部分:一个文件对应一个部分。
+
为cargo test
指定测试函数名称参数也会匹配集成测试文件中的函数。为了只运行某个特定集成测试文件中的所有测试,可以使用cargo test
的--test
参数:
+
$ cargo test --test integration_test
+ Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
+ Running target/debug/integration_test-952a27e0126bb565
+
+running 1 test
+test it_adds_two ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+
+
随着集成测试的增加,你可能希望在 tests
目录增加更多文件,例如根据测试的功能来将测试分组。正如我们之前提到的,这是可以的,Cargo 会将每一个文件当作一个独立的 crate。
+
最终,可能会有一系列在所有集成测试中通用的帮助函数,例如建立通用场景的函数。如果你将这些函数提取到 tests 目录的一个文件中,比如说 tests/common.rs,则这个文件将会像这个目录中的其他包含测试的 Rust 文件一样被编译进一个单独的 crate 中。它也会作为一个独立的部分出现在测试输出中。因为这很可能不是你所希望的,所以建议在子目录中使用 mod.rs 文件,比如 tests/common/mod.rs,来放置帮助函数。tests 的子目录不会被作为单独的 crate 编译或者作为单独的部分出现在测试输出中。
+
+
如果项目是二进制 crate 并且只包含 src/main.rs 而没有 src/lib.rs,这样就不可能在 tests 创建集成测试并使用 extern crate
导入 src/main.rs 中的函数了。这也是 Rust 二进制项目明确采用 src/main.rs 调用 src/lib.rs 中逻辑的结构的原因之一。通过这种结构,集成测试就可以使用extern crate
测试库 crate 中的主要功能,而如果这些功能没有问题的话,src/main.rs 中的少量代码也就会正常工作且不需要测试。
+
+
Rust 的测试功能提供了一个确保即使改变代码函数也能继续以指定方式运行的途径。单元测试独立的验证库的每一部分并能够测试私有实现细节。集成测试则涉及多个部分结合起来时能否使用,并像其他代码那样测试库的公有 API。Rust 的类型系统和所有权规则可以帮助避免一些 bug,不过测试对于减少代码是否符合期望的逻辑 bug 是很重要的。
+
接下来让我们结合本章所学和其他之前章节的知识,在下一章一起编写一个项目!
+
+
+ch12-00-an-io-project.md
+
+commit efd59dd0fe8e3658563fb5fd289af9d862e07a03
+
+
之前几个章节我们学习了很多知识。让我们一起运用这些新知识来构建一个项目。在这个过程中,我们还将学习到更多 Rust 标准库的内容。
+
那么我们应该写点什么呢?这得是一个利用 Rust 优势的项目。Rust 的一个强大的用途是命令行工具:Rust 的运行速度、安全性、“单二进制文件”输出和跨平台支持使得它称为这类工作的绝佳选择。所以我们将创建一个我们自己的经典命令行工具:grep
。grep
有着极为简单的应用场景,它完成如下工作:
+
+- 它获取一个文件和一个字符串作为参数。
+- 读取文件
+- 寻找文件中包含字符串参数的行
+- 打印出这些行
+
+
另外,我们还将添加一个额外的功能:一个环境变量允许我们大小写不敏感的搜索字符串参数。
+
还有另一个很好的理由使用grep
作为示例项目:Rust 社区的成员,Andrew Gallant,已经使用 Rust 创建了一个功能非常完整的grep
版本。它叫做ripgrep
,并且它非常非常快。这样虽然我们的grep
将会非常简单,你也会掌握阅读现实生活中项目的基础知识。
+
这个项目将会结合之前所学的一些内容:
+
+- 代码组织(使用第七章学习的模块)
+- vector 和字符串(第八章,集合)
+- 错误处理(第九章)
+- 合理的使用 trait 和生命周期(第十章)
+- 测试(第十一章)
+
+
另外,我还会简要的讲到闭包、迭代器和 trait 对象,他们分别会在第XX、YY和ZZ章详细介绍。
+
让我们一如既往的使用cargo new
创建一个新项目:
+
$ cargo new --bin greprs
+ Created binary (application) `greprs` project
+$ cd greprs
+
+
我们版本的grep
的叫做“greprs”,这样就不会迷惑用户让他们以为这就是可能已经在系统上安装了功能更完整的grep
。
+
+
+ch12-01-accepting-command-line-arguments.md
+
+commit 2d32840aae46d247250310219e8c7169c7349017
+
+
第一个任务是让greprs
接受两个命令行参数。crates.io 上有一些现存的库可以帮助我们,不过因为我们正在学习,我们将自己实现一个。
+
我们需要调用一个 Rust 标准库提供的函数:std::env::args
。这个函数返回一个传递给程序的命令行参数的迭代器(iterator)。我们还未讨论到迭代器,第十三章会全面的介绍他们。但是对于我们的目的来说,使用他们并不需要知道多少技术细节。我们只需要明白两点:
+
+- 迭代器生成一系列的值。
+- 在迭代器上调用
collect
方法可以将其生成的元素转换为一个 vector。
+
+
让我们试试列表 12-1 中的代码:
+
+
+
首先使用use
语句来将std::env
模块引入作用域。当函数嵌套了多于一层模块时,比如说std::env::args
,通常使用use
将父模块引入作用域,而不是引入其本身。env::args
比单独的args
要明确一些。当然,如果使用了多余一个std::env
中的函数,我们也只需要一个use
语句。
+
在main
函数的第一行,我们调用了env::args
,并立即使用collect
来创建了一个 vector。这里我们也显式的注明了args
的类型:collect
可以被用来创建很多类型的集合。Rust 并不能推断出我们需要什么类型,所以类型注解是必须的。在 Rust 中我们很少会需要注明类型,不过collect
是就一个通常需要这么做的函数。
+
最后,我们使用调试格式:?
打印出 vector。让我们尝试不用参数运行代码,接着用两个参数:
+
$ cargo run
+["target/debug/greprs"]
+
+$ cargo run needle haystack
+...snip...
+["target/debug/greprs", "needle", "haystack"]
+
+
你会注意一个有趣的事情:二进制文件的名字是第一个参数。其原因超出了本章介绍的范围
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 7e91e19..57fb372 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -53,4 +53,12 @@
- [测试](ch11-00-testing.md)
- [编写测试](ch11-01-writing-tests.md)
- [运行测试](ch11-02-running-tests.md)
- - [测试的组织结构](ch11-03-test-organization.md)
\ No newline at end of file
+ - [测试的组织结构](ch11-03-test-organization.md)
+
+- [一个 I/O 项目](ch12-00-an-io-project.md)
+ - [接受命令行参数](ch12-01-accepting-command-line-arguments.md)
+ - [读取文件](ch12-02-reading-a-file.md)
+ - [增强错误处理和模块化](ch12-03-improving-error-handling-and-modularity.md)
+ - [测试库的功能](ch12-04-testing-the-librarys-functionality.md)
+ - [处理环境变量](ch12-05-working-with-environment-variables.md)
+ - [输出到`stderr`而不是`stdout`](ch12-06-writing-to-stderr-instead-of-stdout.md)
\ No newline at end of file
diff --git a/src/ch11-03-test-organization.md b/src/ch11-03-test-organization.md
index 883c33c..75736aa 100644
--- a/src/ch11-03-test-organization.md
+++ b/src/ch11-03-test-organization.md
@@ -120,3 +120,91 @@ Listing 11-6: Testing a private function
### 集成测试
+在 Rust 中,集成测试对于需要测试的库来说是完全独立。他们同其他代码一样使用库文件。他们的目的是测试库的个个部分结合起来能否正常工作。每个能正确运行的代码单元集成在一起也可能会出现问题,所以集成测试的覆盖率也是很重要的。
+
+#### *tests* 目录
+
+Cargo 支持位于 *tests* 目录中的集成测试。如果创建它并放入 Rust 源文件,Cargo 会将每一个文件当作单独的 crate 来编译。让我们试一试!
+
+首先,在项目根目录创建一个 *tests* 目录,挨着 *src* 目录。接着新建一个文件 *tests/integration_test.rs*,并写入列表 11-7 中的代码:
+
+
+
+
+在开头使用了`extern crate adder`,单元测试中并不需要它。`tests`目录中的每一个测试文件都是完全独立的 crate,所以需要在每个文件中导入我们的库。这也就是为何`tests`是编写集成测试的绝佳场所:他们像任何其他用户那样,需要将库导入 crate 并只能使用公有 API。
+
+这个文件中也不需要`tests`模块。除非运行测试否则整个文件夹都不会被编译,所以无需将任何部分标记为`#[cfg(test)]`。另外每个测试文件都被隔离进其自己的 crate 中,无需进一步隔离测试代码。
+
+让我们运行集成测试,同样使用`cargo test`来运行:
+
+```
+$ cargo test
+ Compiling adder v0.1.0 (file:///projects/adder)
+ Running target/debug/deps/adder-abcabcabc
+
+running 1 test
+test tests::it_works ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Running target/debug/integration_test-952a27e0126bb565
+
+running 1 test
+test it_adds_two ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+ Doc-tests adder
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+```
+
+现在有了三个部分的输出:单元测试、集成测试和文档测试。注意当在任何 *src* 目录的文件中增加单元测试时,单元测试部分的对应输出也会增加。增加集成测试文件中的测试函数也会对应增加输出。如果在 *tests* 目录中增加集成测试**文件**,则会增加更多集成测试部分:一个文件对应一个部分。
+
+为`cargo test`指定测试函数名称参数也会匹配集成测试文件中的函数。为了只运行某个特定集成测试文件中的所有测试,可以使用`cargo test`的`--test`参数:
+
+```
+$ cargo test --test integration_test
+ Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
+ Running target/debug/integration_test-952a27e0126bb565
+
+running 1 test
+test it_adds_two ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+```
+
+#### 集成测试中的子模块
+
+随着集成测试的增加,你可能希望在 `tests` 目录增加更多文件,例如根据测试的功能来将测试分组。正如我们之前提到的,这是可以的,Cargo 会将每一个文件当作一个独立的 crate。
+
+最终,可能会有一系列在所有集成测试中通用的帮助函数,例如建立通用场景的函数。如果你将这些函数提取到 *tests* 目录的一个文件中,比如说 *tests/common.rs*,则这个文件将会像这个目录中的其他包含测试的 Rust 文件一样被编译进一个单独的 crate 中。它也会作为一个独立的部分出现在测试输出中。因为这很可能不是你所希望的,所以建议在子目录中使用 *mod.rs* 文件,比如 *tests/common/mod.rs*,来放置帮助函数。*tests* 的子目录不会被作为单独的 crate 编译或者作为单独的部分出现在测试输出中。
+
+#### 二进制 crate 的集成测试
+
+如果项目是二进制 crate 并且只包含 *src/main.rs* 而没有 *src/lib.rs*,这样就不可能在 *tests* 创建集成测试并使用 `extern crate` 导入 *src/main.rs* 中的函数了。这也是 Rust 二进制项目明确采用 *src/main.rs* 调用 *src/lib.rs* 中逻辑的结构的原因之一。通过这种结构,集成测试**就可以**使用`extern crate`测试库 crate 中的主要功能,而如果这些功能没有问题的话,*src/main.rs* 中的少量代码也就会正常工作且不需要测试。
+
+## 总结
+
+Rust 的测试功能提供了一个确保即使改变代码函数也能继续以指定方式运行的途径。单元测试独立的验证库的每一部分并能够测试私有实现细节。集成测试则涉及多个部分结合起来时能否使用,并像其他代码那样测试库的公有 API。Rust 的类型系统和所有权规则可以帮助避免一些 bug,不过测试对于减少代码是否符合期望的逻辑 bug 是很重要的。
+
+接下来让我们结合本章所学和其他之前章节的知识,在下一章一起编写一个项目!
\ No newline at end of file
diff --git a/src/ch12-00-an-io-project.md b/src/ch12-00-an-io-project.md
new file mode 100644
index 0000000..54a22aa
--- /dev/null
+++ b/src/ch12-00-an-io-project.md
@@ -0,0 +1,38 @@
+# 一个 I/O 项目
+
+> [ch12-00-an-io-project.md](https://github.com/rust-lang/book/blob/master/src/ch12-00-an-io-project.md)
+>
+> commit efd59dd0fe8e3658563fb5fd289af9d862e07a03
+
+之前几个章节我们学习了很多知识。让我们一起运用这些新知识来构建一个项目。在这个过程中,我们还将学习到更多 Rust 标准库的内容。
+
+那么我们应该写点什么呢?这得是一个利用 Rust 优势的项目。Rust 的一个强大的用途是命令行工具:Rust 的运行速度、安全性、“单二进制文件”输出和跨平台支持使得它称为这类工作的绝佳选择。所以我们将创建一个我们自己的经典命令行工具:`grep`。`grep`有着极为简单的应用场景,它完成如下工作:
+
+1. 它获取一个文件和一个字符串作为参数。
+2. 读取文件
+3. 寻找文件中包含字符串参数的行
+4. 打印出这些行
+
+另外,我们还将添加一个额外的功能:一个环境变量允许我们大小写不敏感的搜索字符串参数。
+
+还有另一个很好的理由使用`grep`作为示例项目:Rust 社区的成员,Andrew Gallant,已经使用 Rust 创建了一个功能非常完整的`grep`版本。它叫做`ripgrep`,并且它非常非常快。这样虽然我们的`grep`将会非常简单,你也会掌握阅读现实生活中项目的基础知识。
+
+这个项目将会结合之前所学的一些内容:
+
+- 代码组织(使用第七章学习的模块)
+- vector 和字符串(第八章,集合)
+- 错误处理(第九章)
+- 合理的使用 trait 和生命周期(第十章)
+- 测试(第十一章)
+
+另外,我还会简要的讲到闭包、迭代器和 trait 对象,他们分别会在第XX、YY和ZZ章详细介绍。
+
+让我们一如既往的使用`cargo new`创建一个新项目:
+
+```text
+$ cargo new --bin greprs
+ Created binary (application) `greprs` project
+$ cd greprs
+```
+
+我们版本的`grep`的叫做“greprs”,这样就不会迷惑用户让他们以为这就是可能已经在系统上安装了功能更完整的`grep`。
\ No newline at end of file
diff --git a/src/ch12-01-accepting-command-line-arguments.md b/src/ch12-01-accepting-command-line-arguments.md
new file mode 100644
index 0000000..512e61e
--- /dev/null
+++ b/src/ch12-01-accepting-command-line-arguments.md
@@ -0,0 +1,52 @@
+## 接受命令行参数
+
+> [ch12-01-accepting-command-line-arguments.md](https://github.com/rust-lang/book/blob/master/src/ch12-01-accepting-command-line-arguments.md)
+>
+> commit 2d32840aae46d247250310219e8c7169c7349017
+
+第一个任务是让`greprs`接受两个命令行参数。crates.io 上有一些现存的库可以帮助我们,不过因为我们正在学习,我们将自己实现一个。
+
+我们需要调用一个 Rust 标准库提供的函数:`std::env::args`。这个函数返回一个传递给程序的命令行参数的**迭代器**(*iterator*)。我们还未讨论到迭代器,第十三章会全面的介绍他们。但是对于我们的目的来说,使用他们并不需要知道多少技术细节。我们只需要明白两点:
+
+1. 迭代器生成一系列的值。
+2. 在迭代器上调用`collect`方法可以将其生成的元素转换为一个 vector。
+
+让我们试试列表 12-1 中的代码:
+
+
+
+
+
+首先使用`use`语句来将`std::env`模块引入作用域。当函数嵌套了多于一层模块时,比如说`std::env::args`,通常使用`use`将父模块引入作用域,而不是引入其本身。`env::args`比单独的`args`要明确一些。当然,如果使用了多余一个`std::env`中的函数,我们也只需要一个`use`语句。
+
+在`main`函数的第一行,我们调用了`env::args`,并立即使用`collect`来创建了一个 vector。这里我们也显式的注明了`args`的类型:`collect`可以被用来创建很多类型的集合。Rust 并不能推断出我们需要什么类型,所以类型注解是必须的。在 Rust 中我们很少会需要注明类型,不过`collect`是就一个通常需要这么做的函数。
+
+最后,我们使用调试格式`:?`打印出 vector。让我们尝试不用参数运行代码,接着用两个参数:
+
+```
+$ cargo run
+["target/debug/greprs"]
+
+$ cargo run needle haystack
+...snip...
+["target/debug/greprs", "needle", "haystack"]
+```
+
+你会注意一个有趣的事情:二进制文件的名字是第一个参数。其原因超出了本章介绍的范围
\ No newline at end of file
diff --git a/src/ch12-02-reading-a-file.md b/src/ch12-02-reading-a-file.md
new file mode 100644
index 0000000..e69de29
diff --git a/src/ch12-03-improving-error-handling-and-modularity.md b/src/ch12-03-improving-error-handling-and-modularity.md
new file mode 100644
index 0000000..e69de29
diff --git a/src/ch12-04-testing-the-librarys-functionality.md b/src/ch12-04-testing-the-librarys-functionality.md
new file mode 100644
index 0000000..e69de29
diff --git a/src/ch12-05-working-with-environment-variables.md b/src/ch12-05-working-with-environment-variables.md
new file mode 100644
index 0000000..e69de29
diff --git a/src/ch12-06-writing-to-stderr-instead-of-stdout.md b/src/ch12-06-writing-to-stderr-instead-of-stdout.md
new file mode 100644
index 0000000..e69de29