编辑后,发现需要root权限

:w ! sudo tee %

做计算

insert模式,ctrl+r =3600*24*7

查看字符ascii码,16进制,8进制

common模式,ga 查看光标下的字符的ascii码	

插入unicode字符

大小写转换

~          将光标下的字母改变大小写
3m~         将光标位置开始的3个字母改变其大小写
g~~        改变当前行字母的大小写
U          将可视模式下选择的字母全改成大写字母
u          将可视模式下选择的字母全改成小写
gUU        将当前行的字母改成大写
3gUU       将从光标开始到下面3行字母改成大写
guu       将当前行的字母全改成小写
gUw       将光标下的单词改成大写。
guw       将光标下的单词改成小写。

Insert mode 缩进

ctrl+t >	
crtl+d <

!的作用

- 切换布尔选项,:set number!
- 执行shell命令 :! ls

牛人的话(翻译后)

Adrian Kuhn et.al:

写单元测试是一个非常好的做法,帮助开发定位和修复bug,重构代码并作为测试软件的一个单元的文档。
为了能达到这个效果,单元测试要考虑到程序中所有路径。一个方法或函数通常只覆盖一个特殊的路径。
然而,一个测试方法并不是一个封闭的,独立的实体。通常在测试方法之间的隐式依赖,就藏在某个测试场景的实现中。

Test Dependencies

  • producer: 返回值给其他方法使用的方法
  • consumer: 依靠别的方法和它们的返回值的方法
  • 一个方法,可以兼顾这个两个角色
  • consumer方法需要在方法注释中标注@depends testFunc
  • 一个consumer方法可以一个或多个@depends,当有多个@depends时,@depends的声明顺序就是consumer参数顺序
    • 参数的传递,默认是引用传参
    • @depends clone 就是深复制,传值传参
    • @depends shallowClone 浅复制
  • consumer方法有多个@depends时,要确保这些producer方法要写在consumer的前面,不然就会出现skip

代码如下:

<?php
use PHPUnit\Framework\TestCase;

class MultipleDependenciesTest extends TestCase
{
	public function testProducerFirst()
	{
		$this->assertTrue(true);
		return 'first';
	}

	public function testProducerSecond()
	{
		$this->assertTrue(true);
		return 'second';
	}

	/**
	 * @depends testProducerFirst
	 * @depends testProducerSecond
	 */
	public function testConsumer($a, $b)
	{
		$this->assertSame('first', $a);
		$this->assertSame('second', $b);
	}
}

Data Providers

  • 某个方法的注释中用 @dataProvider 这个特殊注释 标识了某个方法,则这个方法就是data provider
    • @dataProvider testFunc testFunc就是data provider
  • data provider返回值必须要是n维数组(n>=2),或可以迭代的对象(Iterator),并且这个对象每一次迭代都要返回一个数组
  • 测试方法的参数可以由一个或多个data provider 提供
  • 当测试方法同时有@dataProvider和@depends时,@dataProvider的参数要先于@depends

  • demo
<?php
use PHPUnit\Framework\TestCase;

class DependencyAndDataProviderComboTest extends TestCase
{
	public function provider()
	{
		return [['provider1'], ['provider2']];
	}

	public function testProducerFirst()
	{
		$this->assertTrue(true);
		return 'first';
	}

	public function testProducerSecond()
	{
		$this->assertTrue(true);
		return 'second';
	}

	/**
	 * @depends testProducerFirst
	 * @depends testProducerSecond
	 * @dataProvider provider
	 */
	public function testConsumer()
	{
		$this->assertSame(
			['provider1', 'first', 'second'],
			func_get_args()
			/**
			func_get_args()依次是
			['provider1', 'first', 'second'],
			['provider2', 'first', 'second']
			因为 provider迭代了两次,第一次返回['provider1'], 字符串'provider1'就传递给了testConsumer,作为第一个参数,
			**/
		);
	}
}

sequenceDiagram participant MasterG2 participant Master participant Dev as F分支 participant Bg0 participant Bg2 as Bg2/F2 participant Test participant Release participant ReleaseG2 Note over Master,Release:特性分支(f分支)开发流程 Master ->>+ Dev:开发拉f分支 Dev -->> Dev: 开发自测 loop 提测 Dev ->> Test:F分支合并到Test分支 Note over Test:是否有冲突 alt 没有有冲突 Note over Test :jenkins构建test环境 Note over Test:验证测试环境 alt 测试环境验证通过 Note over Dev :F分支能否合并到Release分支 alt 能合并 Dev ->> Release:F分支合并到Release Note over Release :是否有冲突 alt 没有冲突 Note over Release :jenkins构建beta g0 Note over Release:验证beta g0环境 alt beta g0环境验证通过 Note over Release: 积累一天 Note over Release : jenkins构建prod g0 Note over Release : 验证prod g0 alt prod g0验证通过 Release ->>- Dev : 删除所有的合并到Relase的F分支 Release ->> Master : Release分支合并到Master else prod g0没有验证通过 Release -->> Dev: prod g0验证没有通过,排查原因 end else Release -->> Dev: beta g0测试不通过, 排查问题 end else 有冲突 Release -->> Dev : 开发同学先解决冲突,再次提测 end else F分支不能合并到Release,原因: Note over Dev: 过了14:00 Note over Dev: 距离灰度发布不足两天 Note over Dev: 灰度发布期间 end else 测试环境验证不通过 Test -->> Dev:测试环境不通过,排查问题 end else 有冲突 Test -->> Dev :先解决冲突,再提测 end end Note over Master,Release:Bg0分支开发流程(解决线上g0环境的bug) Note over Release: 灰度前后验证prod G0环境 alt prod g0环境验证通过 else prod g0验证不通过 Master ->>+ Bg0 : 从Master拉取一个Bg0分支 Bg0 -->> Bg0 : 开发自测 Bg0 ->> Test : 合并到Test分支 alt Bg0合并到Test没有冲突 Note over Test: jenkins 构建测试环境,并验证 alt 测试环境验证通过 Bg0 ->> Release: Bg0合并到Release分支 alt 合并没有冲突 Note over Release :jenkins构建beta g0环境,并验证 alt beta g0验证通过 Note over Release:构建 prod g0,并验证 alt prod g0通过 Release ->> Master: 合并到Master分支,删除Bg0分支 deactivate Bg0 else 失败 Release -->> Bg0 : prod g0未通过,排查原因,再提测 end else 失败 Release -->> Bg0: beta g0未通过,排查原因,再提测 end else Bg0合并Release发生冲突 Release -->> Bg0: 解决冲突,重新提测 end else 测试环境验证不通过 Test -->> Bg0: 查找原因,重新提测 end else Bg0合并到Test有冲突 Test -->> Bg0:解决冲突,重新提测 end end Note over MasterG2, ReleaseG2 : 全量发布 Note right of ReleaseG2:prod环境全量发布 Note over Release,ReleaseG2 :把prod g2的租户漂移到prod g0环境 Release ->> ReleaseG2: Release分支合并到RelaseG2 Note over ReleaseG2:jenkins构建prod g2环境 Note over Release,ReleaseG2 :把大部分prod g0租户漂移回prod g2环境 Note right of ReleaseG2:beta环境全量发布 Note over Release,ReleaseG2 :把beta g2的租户漂移到beta g0环境 Note over ReleaseG2:jenkins构建beta g2环境 Note over Release,ReleaseG2 :把大部分beta g0租户漂移回beta g2环境 ReleaseG2 ->> MasterG2 : 全量发布完成,ReleaseG2合并到MasterG2 Note over MasterG2,ReleaseG2:Bg2/F2分支开发流程(解决线上g2环境的bug(Bg2分支),或G2环境的需求(F2分支)) MasterG2 ->>+ Bg2: 拉取Bg2/F2分支 Bg2 -->> Bg2: 开发自测 Bg2 ->> Test: 合并到test分支 Note over Test:jenkins构建测试环境,并验证 alt 测试环境验证通过 Bg2 ->> ReleaseG2 : 合并到ReleaseG2分支
如果还有别的bg2/f2分支
在betag2/prodg1验证中,就要等待 alt 合并成功,没有冲突 Note over ReleaseG2:jenkins构建beta g2环境 Note over ReleaseG2:在beta g2环境验证 alt beta g2环境验证通过 Note over ReleaseG2: jenkins构建prod g1环境 Note right of ReleaseG2: 从G2选取部分租户
漂移到prod g1 alt prod g1租户验证不通过 ReleaseG2 -->> Bg2:验证不通过,查原因,重新提测 else prod g1的租户验证通过 Note over ReleaseG2: 在prod g1等待 ReleaseG2 ->> Release : 合并到Release ReleaseG2 ->> MasterG2: 合并到MasterG2
并删除Bg2分支 deactivate Bg2 Note over ReleaseG2: 别的bg2/f2分支
可以合并到ReleaseG2分支
进行验证了 end Note right of ReleaseG2: 租户在prod g1上
过了观察期(如1天) Note right of ReleaseG2: 把租户全部漂移
到prod g1 Note over ReleaseG2: jenkins构建prod g2环境 Note right of ReleaseG2: 把prod g1租户
全部漂移到prod g2 else beta g2环境验证不通过 ReleaseG2 -->> Bg2: beta g2验证不通过,查原因,重新提测 end else Bg2/F2合并ReleaseG2,发生冲突 ReleaseG2 -->> Bg2:解决冲突,重新接测 end else Test -->> Bg2:测试环境不通过,查原因,重新提测 end

大牛的话(翻译后)

Martin Fowler:

无论什么时候,当你试着在print或其他debugger表达式里输入内容时,你应该把它写成一个测试用例

单元测试是代码质量的第一道关口。做好了单元测试,有利于尽早发现问题,解决问题。对CI/CD来说也是很重要的一个环节。当然,写单元测试会增加编写代码的工作量,所以一些公司或项目为了赶工期,单元测试就被忽略了,尽管大家都知道这个很重要。出来混,迟早要还的。欠下的,会加倍偿还。特别是随着产品的迭代,这个问题会逐渐凸显出来。

先来說一下php的单元测试,使用的是PHPUnit

Install

参考文档:https://phpunit.readthedocs.io/en

linux下简单的几个步骤如下

$wget https://phar.phpunit.de/phpunit-7.5.2.phar
$chmod +x phpunit-7.5.2.phar
$sudo mv phpunit-7.5.2.phar /usr/local/bin/phpunit
$phpunit --version
PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

小试牛刀

创建一个文件比如test.php,内容如下

<?php
use PHPUnit\Framework\TestCase;

class StackTest extends TestCase
{
	public function testPushAndPop()
	{
		$stack = [];
		$this->assertSame(0, count($stack));

		array_push($stack, 'foo');
		$this->assertSame('foo', $stack[count($stack)-1]);
		$this->assertSame(1, count($stack));

		$this->assertSame('foo', array_pop($stack));
		$this->assertSame(1, count($stack));
	}

	/**
	* @test
	*/
	public function num()
	{
		$this->assertSame(1,1);
		$this->assertEquals(1,'1');
		$this->assertEquals(1,1.0);
	}
}

执行测试phpunit ./test.php,返回结果如下

PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

F.                                                                  2 / 2 (100%)

Time: 183 ms, Memory: 10.00MB

There was 1 failure:

1) StackTest::testPushAndPop
Failed asserting that 0 is identical to 1.

/home/wm/code/php/phpunit/test.php:16

FAILURES!
Tests: 2, Assertions: 8, Failures: 1.

测试结果告诉我们,有两个测试方法,有8处断言,有一处失败,失败的原因是断言0和1相等,在代码的第16行。

我们修改16行为: $this->assertSame(0, count($stack));再执行测试

PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 205 ms, Memory: 10.00MB

OK (2 tests, 8 assertions)

通过了测试^_^

几点说明

1.测试的类应该命名格式为ClassTest,大驼峰,而且以Test结尾,文件名和类名相同(我个人推测)
2.测试的类要继承PHPUnit\Framework\TestCase(大多数情况下)
3.测试的方法都要是public,声明一个方法是否是测试方法有如下两个方法
	- 方法名称的格式:test*
	- 或则在方法的注释中添加@test,来标识该方法是测试方法,如上
4.在测试方法里有很多断言方法,如上assertSame(),assertEquals()
5.出现失败后,就不会继续执行了。

sequenceDiagram participant MasterG2 participant Master participant Dev as F分支 participant Bg0 participant Bg2 as Bg2/F2 participant Test participant Release participant ReleaseG2 Note over Master,Release:特性分支(f分支)开发流程 Master ->>+ Dev:开发拉f分支 Dev -->> Dev: 开发自测 loop 提测 Dev ->> Test:F分支合并到Test分支 Note over Test:是否有冲突 alt 没有有冲突 Note over Test :jenkins构建test环境 Note over Test:验证测试环境 alt 测试环境验证通过 Note over Dev :F分支能否合并到Release分支 alt 能合并 Dev ->> Release:F分支合并到Release Note over Release :是否有冲突 alt 没有冲突 Note over Release :jenkins构建beta g0 Note over Release:验证beta g0环境 alt beta g0环境验证通过 Note over Release: 积累一天 Note over Release : jenkins构建prod g0 Note over Release : 验证prod g0 alt prod g0验证通过 Release ->>- Dev : 删除所有的合并到Relase的F分支 Release ->> Master : Release分支合并到Master else prod g0没有验证通过 Release -->> Dev: prod g0验证没有通过,排查原因 end else Release -->> Dev: beta g0测试不通过, 排查问题 end else 有冲突 Release -->> Dev : 开发同学先解决冲突,再次提测 end else F分支不能合并到Release,原因: Note over Dev: 过了14:00 Note over Dev: 距离灰度发布不足两天 Note over Dev: 灰度发布期间 end else 测试环境验证不通过 Test -->> Dev:测试环境不通过,排查问题 end else 有冲突 Test -->> Dev :先解决冲突,再提测 end end Note over Master,Release:Bg0分支开发流程(解决线上g0环境的bug) Note over Release: 灰度前后验证prod G0环境 alt prod g0环境验证通过 else prod g0验证不通过 Master ->>+ Bg0 : 从Master拉取一个Bg0分支 Bg0 -->> Bg0 : 开发自测 Bg0 ->> Test : 合并到Test分支 alt Bg0合并到Test没有冲突 Note over Test: jenkins 构建测试环境,并验证 alt 测试环境验证通过 Bg0 ->> Release: Bg0合并到Release分支 alt 合并没有冲突 Note over Release :jenkins构建beta g0环境,并验证 alt beta g0验证通过 Note over Release:构建 prod g0,并验证 alt prod g0通过 Release ->> Master: 合并到Master分支,删除Bg0分支 deactivate Bg0 else 失败 Release -->> Bg0 : prod g0未通过,排查原因,再提测 end else 失败 Release -->> Bg0: beta g0未通过,排查原因,再提测 end else Bg0合并Release发生冲突 Release -->> Bg0: 解决冲突,重新提测 end else 测试环境验证不通过 Test -->> Bg0: 查找原因,重新提测 end else Bg0合并到Test有冲突 Test -->> Bg0:解决冲突,重新提测 end end Note over MasterG2, ReleaseG2 : 全量发布 Note right of ReleaseG2:prod环境全量发布 Note over Release,ReleaseG2 :把prod g2的租户漂移到prod g0环境 Release ->> ReleaseG2: Release分支合并到RelaseG2 Note over ReleaseG2:jenkins构建prod g2环境 Note over Release,ReleaseG2 :把大部分prod g0租户漂移回prod g2环境 Note right of ReleaseG2:beta环境全量发布 Note over Release,ReleaseG2 :把beta g2的租户漂移到beta g0环境 Note over ReleaseG2:jenkins构建beta g2环境 Note over Release,ReleaseG2 :把大部分beta g0租户漂移回beta g2环境 ReleaseG2 ->> MasterG2 : 全量发布完成,ReleaseG2合并到MasterG2 Note over MasterG2,ReleaseG2:Bg2/F2分支开发流程(解决线上g2环境的bug(Bg2分支),或G2环境的需求(F2分支)) MasterG2 ->>+ Bg2: 拉取Bg2/F2分支 Bg2 -->> Bg2: 开发自测 Bg2 ->> Test: 合并到test分支 Note over Test:jenkins构建测试环境,并验证 alt 测试环境验证通过 Bg2 ->> ReleaseG2 : 合并到ReleaseG2分支 alt 合并成功,没有冲突 Note over ReleaseG2:jenkins构建beta g2环境 Note over ReleaseG2:在beta g2环境验证 alt beta g2环境验证通过 Note over ReleaseG2: jenkins构建prod g2环境 alt prod g2验证通过 ReleaseG2 ->> Release : 合并到Release ReleaseG2 ->> MasterG2: 合并到MasterG2,并删除Bg2分支 deactivate Bg2 else ReleaseG2 -->> Bg2: prod g2验证没有通过 end else beta g2环境验证不通过 ReleaseG2 -->> Bg2: beta g2验证不通过,查原因,重新提测 end else Bg2/F2合并ReleaseG2,发生冲突 ReleaseG2 -->> Bg2:解决冲突,重新接测 end else Test -->> Bg2:测试环境不通过,查原因,重新提测 end