学习单元测试,告别祈祷式编程

[TOC] 祈祷式编程 祈祷式编程 如果代码中包含以下代码 或者上线后进行这种活动 那么这种编程方式就是祈祷式编程。 用流程图表示基本就是这个样子。 祈祷式编程有什么危害呢? 累,每次写完代码还需要再祈祷 不受控,代码运行结果主要看运气,大仙忙的时候可能保佑不了 解决这个问题有好多种方法,单元测试是其中之一。 单元测试 什么是单元测试 单元测试是由开发人员编写的,用于对软件基本单元进行测试的可执行的程序。 单元(unit)是一个应用程序中最小的课测试部分。(比如一个函数,一个类 google 把测试分成小型测试、中型测试和大型测试。单元测试基本和小型测试的作用类似,但是通常也会使用mock或者stub 的方式模拟外部服务。 理想情况下,单元测试应该是相互独立、可自动化运行的。 目的: 通常用单元测试来验证代码逻辑是否符合预期。完整可靠的单元测试是代码的安全网,可以在代码修改或重构时验证业务逻辑是否正确,提前发现代码错误,减少调试时间。设计良好的单元测试某些情况下可以比文档更能反应出代码的功能和作用。 单元测试这么多优点为什么有人不喜欢写单元测试呢? 单元测试太费时间了,对于编写单元测试不熟练的新手来说,编写单元测试可能比写代码的还费时间 单元测试运行时间太长(这通常是单元测试设计不合理或者代码可测试性较差造成的 祖传代码,看都看不懂怎么写单元测试(这个确实优点棘手。。可以考虑先给新代码加单元测试 不会写单元测试 这篇文章主要关注第四个问题,如何写单元测试。 单元测试的结构 首先看一下单元测试的结构,一个完整的单元测试主要包括Arrange-Act-Assert(3A) 三部分。 Arrange–准备数据 Act–运行代码 Assert–判断结果是否符合预期 比如我们要给下面这段代码(golang)加单元测试: func Add(x, y int) int { return x + y } 单元测试代码如下: import "testing" func TestAdd(t *testing.T) { // arrange 准备数据 x, y := 1, 2 // act 运行 got := Add(x, y) //assert 断言 if got != 3 { t.Errorf("Add() = %v, want %v", got, 3) } } 如何编写好的单元测试 什么样的单元测试才是好的单元测试呢? ...

2019-10-07 · 3 min · 625 words

如何通过MD5反查身份证号?

题目:设计一个身份证查询系统,将身份证号md5 之后存储,输入md5值查询对应的身份证号。 要求:成本低,查询速度快 设计思路: 将所有可能的身份证号做一个简单的统计计算数据量 根据数据量选择存储方式 查询 身份证生成规则: 身份号码是特征组合码,由前十七位数字本体码和最后一位数字校验码组成。排列顺序从左至右依次为六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。 地址码: 表示编码对象常住户口所在县(市、旗、区)的行政区划代码。对于新生儿,该地址码为户口登记地行政区划代码。需要没说明的是,随着行政区划的调整,同一个地方进行户口登记的可能存在地址码不一致的情况。行政区划代码按GB/T2260的规定执行。 出生日期码:表示编码对象出生的年、月、日,年、月、日代码之间不用分隔符,格式为YYYYMMDD,如19880328。按GB/T 7408的规定执行。原15位身份证号码中出生日期码还有对百岁老人特定的标识,其中999、998、997、996分配给百岁老人。 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。 校验码: 根据本体码,通过采用ISO 7064:1983,MOD 11-2校验码系统计算出校验码。算法可参考下文。前面有提到数字校验码,我们知道校验码也有X的,实质上为罗马字符X,相当于10. 校验码算法 将本体码各位数字乘以对应加权因子并求和,除以11得到余数,根据余数通过校验码对照表查得校验码。 加权因子表: +-----------------------------------------------------------+ |位置序号|1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11|12|13|14|15|16|17| +-----------------------------------------------------------+ |加权因子|7 |9 |10|5 |8 |4 |2 |1 |6 |3 |7 |9 |10|5 |8 |4 |2 | +-----------------------------------------------------------+ 校验码表: +----------------------------------------------------+ | 余数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +----------------------------------------------------+ | 校验码| 1 | 0 | X | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | +----------------------------------------------------+ 算法举例: 本体码为11010519491231002 ...

2019-08-18 · 1 min · 199 words

markdown中code生成图片的实现

前几天写了《markdown 生成头条文章的一个思路》,周末就试了试。 先回顾一下思路,大致流程如下: 这里的三个关键点是: 提取code 把code 转换为html 把html 生成图片 code 替换成图片 第一个很简单,只有用正则表达式就可以解决: _fenced_code_block_re = re.compile(r''' (?:\n+|\A\n?) ^```\s*?([\w+-]+)?\s*?\n # opening fence, $1 = optional lang (.*?) # $2 = code block content ^```[ \t]*\n # closing fence ''', re.M | re.X | re.S) 这个正则来自 python-markdown2: https://github.com/trentm/python-markdown2 这个正则只匹配了 ``` 样式的代码,对于前边有四个空格的并没有做处理(也不想做处理,还是严格一点好)。 第二个也不麻烦,只需要把提取出的code 放到html 中,下面是一个html模板: <html> <head> <link rel="stylesheet" href="http://media.gusibi.mobi/highlight/static/styles/atom-one-dark.css"> <script src="http://media.gusibi.mobi/highlight/static/highlight.site.pack.js"></script> <script>hljs.initHighlightingOnLoad();</script> </head> <body style="width: 640px;"> <pre> <code class="{{.Language}}">{{.Code}}</code> </pre> </body> </html>` 这里有一个点是渲染html 页面的时候, 由于加载html 页面的工具都是get请求,这里我们需要先把code 数据保存起来。所以请求code 的html 页面分成了两步。 ...

2019-06-15 · 2 min · 260 words

markdown中code生成图片的思路

最近在头条上写东西,遇到了一个比较烦的事情—编辑器不支持代码。这对于一个像我这样使用代码凑字数的人来说实在不是一个好的消息。但是等头条改进编辑器太遥远了,只能自己自足实现一个替代方案了–把代码替换成图片。 一段代码的时候,我随手截图,简单完成了; 两段代码的时候,我随手随手截图,也完成了; 三段代码的时候,我随手随手随手截图,强忍着完成了; 等我发现代码越来越多的时候,不能忍了。 懒惰是程序员的美德,不能再花费时间干这些事情了。我觉得要写个程序,把markdown 中的代码自动生成图片。 考虑了一下,大概需要做的工作是: 把markdown 中 “ ” 包换的代码提取出来(也可以使用工具先把markdown 转换成html 再解析html 取出code 把每一段code 分别生成图片 把图片对应的代码替换掉 想想还是很简单的。那就开始吧。 但是到第二步的时候遇到了问题,code 如何生成图片,生成什么样的图片? 首先code 需要保持原有的样式,如果能高亮那就更好了(嗯,高亮 生成图片的时候是把code 作为文字使用PIL(我使用python)写在背景上么,图片大小是多少,高亮怎么实现 算了,还是先把code 生成html,然后截取html页面吧。(这样html 还能使用 highlight.js 来实现高亮) 如何动态生成包含code 的html 页面呢? 如何把截取html 页面呢? 动态生成包含code 的html 页面有两个思路: 使用post 请求,把code 写入数据库(或者文件),然后返回id,再使用id GET 请求获取页面(需要存储,两次请求) 压缩code,把code 作为url参数,使用GET请求获取页面(可能会造成url太长的错误) 那如何截取html呢? 如果是python,可以使用pyqt,渲染html页面,截取webview。 如果使用node,可以使用 html2canvas。 大致流程如下: 哎,这一篇没有代码,就凑不了多少字。 最后,感谢女朋友支持和包容,比❤️ 也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程 内推时间

2019-06-13 · 1 min · 59 words

Golang mod 入门

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为: 模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。 如何使用 Modules ? 把 golang 升级到 1.11(现在1.12 已经发布了,建议使用1.12) 设置 GO111MODULE GO111MODULE GO111MODULE 有三个值:off, on和auto(默认值)。 GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。 GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。 GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形: 当前目录在GOPATH/src之外且该目录包含go.mod文件 当前文件在包含go.mod文件的目录下面。 当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。 go mod golang 提供了 go mod命令来管理包。 go mod 有以下命令: 命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖) 如何在项目中使用 示例一:创建一个新项目 在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件 ➜ ~ mkdir hello ➜ ~ cd hello ➜ hello go mod init hello go: creating new go.mod: module hello ➜ hello ls go.mod ➜ hello cat go.mod module hello go 1.12 go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。 ...

2019-03-16 · 4 min · 689 words

Golang 学习笔记-2:控制流

上一篇我们了解了golang 的变量、函数和基本类型,这一篇将介绍一下控制流 现在我们看一个复杂点的例子: fibonacci(递归版) package main import "fmt" func main() { result := 0 for i := 0; i <= 10; i++ { result = fibonacci(i) fmt.Printf("fibonacci(%d) is: %d\n", i, result) } } func fibonacci(n int) (res int) { if n <= 1 { res = 1 } else { res = fibonacci(n-1) + fibonacci(n-2) } return } // outputs fibonacci(0) is: 1 fibonacci(1) is: 1 fibonacci(2) is: 2 fibonacci(3) is: 3 fibonacci(4) is: 5 fibonacci(5) is: 8 fibonacci(6) is: 13 fibonacci(7) is: 21 fibonacci(8) is: 34 fibonacci(9) is: 55 fibonacci(10) is: 89 for i := 0; i <= 10; i++ {} 第7行是一个循环结构 这里for 循环是一个控制流 控制流 For Go 只有一种循环接口– for 循环 ...

2018-04-17 · 5 min · 896 words

Golang 学习笔记-1:变量&函数

变量&函数 最近在学习golang,写下学习笔记提升记忆。 为了看起来不是那么枯燥,本学习笔记采用分析代码的形式。 首先搬出我们最经典的第一段代码: hello world package main // 0 import "fmt" // 1实现格式化的 I/O /* Print something */ // 2 func main() { // 3 fmt.Println("Hello, world; or καλημε ́ρα κóσμε; orこんにちは 世界") // 4 } 首先我们要认识到 每个Go 程序都是由包组成,程序的运行入口是包main 首行这个是必须的。所有的 Go 文件以 package 开头,对于独立运行的执行文件必须是 package main; 这是说需要将fmt加入到main。不是main 的包被称为库 末尾以 // 开头的内容是单行注释 Package fmt包含有格式化I/O函数,类似于C语言的printf和scanf 这也是注释,表示多行注释。 package main 必须首先出现,紧跟着是 import。在 Go 中,package 总是首先出现, 然后是 import,然后是其他所有内容。当 Go 程序在执行的时候,首先调用的函数 是 main.main(),这是从 C 中继承而来。这里定义了这个函数 调用了来自于 fmt 包的函数打印字符串到屏幕。字符串由 " 包裹,并且可以包含非 ASCII 的字符。这里使用了希腊文和日文、中文" 编译和运行代码 构建 Go 程序的最佳途径是使用 go 工具。 构建 helloworld 只需要: ...

2018-02-22 · 5 min · 968 words

跟着代码学go 002 -- 控制流

控制流 上一篇我们了解了golang 的变量、函数和基本类型,这一篇将介绍一下控制流 现在我们看一个复杂点的例子: HUGOMORE42 fibonacci(递归版) 01 package main 02 03 import "fmt" 04 05 func main() { 06 result := 0 07 for i := 0; i <= 10; i++ { 08 result = fibonacci(i) 09 fmt.Printf("fibonacci(%d) is: %d\n", i, result) 10 } 11 } 12 13 func fibonacci(n int) (res int) { 14 if n <= 1 { 15 res = 1 16 } else { 17 res = fibonacci(n-1) + fibonacci(n-2) 18 } 19 return 20 } // outputs fibonacci(0) is: 1 fibonacci(1) is: 1 fibonacci(2) is: 2 fibonacci(3) is: 3 fibonacci(4) is: 5 fibonacci(5) is: 8 fibonacci(6) is: 13 fibonacci(7) is: 21 fibonacci(8) is: 34 fibonacci(9) is: 55 fibonacci(10) is: 89 for i := 0; i <= 10; i++ {} 第7行是一个循环结构 这里for 循环是一个控制流 控制流 For Go 只有一种循环接口– for 循环 ...

2016-04-26 · 4 min · 836 words

跟着代码学go 001 -- 变量&函数

变量&函数 最近在学习golang,写下学习笔记提升记忆。 为了看起来不是那么枯燥,本学习笔记采用分析代码的形式。 首先搬出我们最经典的第一段代码: HUGOMORE42 hello world package main // 0 import "fmt" // 1实现格式化的 I/O /* Print something */ // 2 func main() { // 3 fmt.Println("Hello, world; or καλημε ́ρα κóσμε; orこんにちは 世界") // 4 } 首先我们要认识到 每个Go 程序都是由包组成,程序的运行入口是包main 首行这个是必须的。所有的 Go 文件以 package 开头,对于独立运行的执行文件必须是 package main; 这是说需要将fmt加入到main。不是main 的包被称为库 末尾以 // 开头的内容是单行注释 Package fmt包含有格式化I/O函数,类似于C语言的printf和scanf 这也是注释,表示多行注释。 package main 必须首先出现,紧跟着是 import。在 Go 中,package 总是首先出现, 然后是 import,然后是其他所有内容。当 Go 程序在执行的时候,首先调用的函数 是 main.main(),这是从 C 中继承而来。这里定义了这个函数 调用了来自于 fmt 包的函数打印字符串到屏幕。字符串由 " 包裹,并且可以包含非 ASCII 的字符。这里使用了希腊文和日文、中文" 编译和运行代码 构建 Go 程序的最佳途径是使用 go 工具。 构建 helloworld 只需要: ...

2016-04-25 · 5 min · 922 words

golang 介绍

1. Golang 是什么 Go 官方说明: Go 编程语言是一个使得程序员更加有效率的开源项目。Go 是有表达力、简 洁、清晰和有效率的。它的并行机制使其很容易编写多核和网络应用,而新的类型系统允许构建有 性的模块化程序。Go 编译到机器码非常快 速,同时具有便利的垃圾回收和强大的运行时反射。它是快速的、静态类 型编译语言,但是感觉上是动态类型的,解释型语言 HUGOMORE42 2. 为什么要开发这个语言 Go 语言的发展目标 Go 语言的主要目标是将静态语言的安全性和高效性与动态语言的易开发性进行有机结合 Go 语言是一门类型安全和内存安全的编程语言。虽然 Go 语言中仍有指针的存在,但并不允许进行指针运算。 Go 语言的另一个目标是对于网络通信、并发和并行编程的极佳支持,从而更好地利用大量的分布式和多核的计算机 Go 语言中另一个非常重要的特性就是它的构建速度(编译和链接到机器代码的速度),一般情况下构建一个程序的时间只需要数百毫秒到几秒。 Go 语言实现高效快速的垃圾回收(使用了一个简单的标记-清除算法)。 3. 有什么特性&&用途 语言的特性 清晰并且简洁 Go 努力保持小并且优美,你可以在短短几行代码里做许多事情; 并行 Go 语言从本质上(程序和结构方面)来实现并发编程。 Go 让函数很容易成为非常轻量的线程。这些线程在 Go 中被叫做 goroutines Channel 这些 goroutines 之间的通讯由 channel[18, 25] 完成 快速 编译很快,执行也很快。目标是跟 C 一样快。编译时间用秒计算; Go 的可执行文件都比相对应的源代码文件要大很多,这恰恰说明了 Go 的 runtime 嵌入到了每一个可执行文件当中。当然,在部署到数量巨大的集群时,较大的文件体积也是比较头疼的问题。但总得来说,Go 的部署工作还是要比 Java 和 Python 轻松得多。因为 Go 不需要依赖任何其它文件,它只需要一个单独的静态文件,这样你也不会像使用其它语言一样在各种不同版本的依赖文件之间混淆。 安全 当转换一个类型到另一个类型的时候需要显式的转换并遵循严格的规则。Go 有 垃圾收集(使用了一个简单的标记-清除算法),在 Go 中无须 free(),语言会处理这一切; 值得注意的是,因为垃圾回收和自动内存分配的原因,Go 语言不适合用来开发对实时性要求很高的软件。 ...

2016-04-14 · 2 min · 271 words