木头的博客

我是木头 有些想法 有点精力

标题党,勿全信;
这是一篇关于前端单元测试的优化心得,讲述了一个中型团队坎坷的痛与泪的故事;
长文预警,建议当短篇小说看;
干货在末尾。

就在前几天,我拿到了心心念念的 MacBook M1 Pro,上手第一件事就是跑一下项目上的前端测试,期待着久仰大名的 M1 处理器一显神威。

为什么这么着急用 M1 跑测试?这个故事要从半年前说起。

1. 越来越痛的测试

小半年前,项目规模越来越大,随着十几轮冲刺后,我们有两个前端库的代码量和测试也越来越多,于是出现了上面的对话

不知从什么时候开始,项目代码里的测试成了我们的痛点,推代码时会触发全量的测试,而在这推代码的10多分钟里,我是什么都做不了的,电脑卡的要命,不得不站起来去接杯水或上个厕所。

从某种角度来说,也挺好,能时不时的提醒我站起来活动活动,放松一下肩颈和眼睛,看看博客摸摸鱼(不是)。

但问题是 Pipeline 上的速度也是稳定慢,从开始 Build 到 Deploy to Dev,更是达到了离谱的 30 分钟左右,Pipeline 甚至还触发了客户设定的 300 小时/月的限流。

2. 法外狂徒

git push --no-verify 成了我们项目的常态。

阅读全文 »

最近把家里的网络折腾了一下,整体用下来感觉还不错,这里记录下来分享给大家,大家一起学习交流~

Infrastructure

痛点:

  • 科学上网。现在家里的网络总是觉得有些不尽人意,本人最大的爱好就是周末宅在家里搞搞开源、打打游戏。遇到问题了 Google / StackOverflow 效率是比 Baidu 高,而每个设备自己科学上网比较繁琐,需要各自管理白名单,太麻烦…
  • NAS 访问慢。和爱人出去玩偶尔会拍一些照片和 Vlog,这些都是美好珍贵的记忆,目前存在自己的私有云上,想看了拿出来看看,照片列表加载缓慢,视频进度拖拉卡顿…
  • WIFI 信号。之前路由器是放在客厅,信号到卧室床头比较曲折,有时关门后甚至断网,网络体验极差…

折腾后:

  • 全屋设备科学上网。在软路由里根据 IP 地址分流,国外 IP 走代理,国内直连。终端无需单独开软件,无需在意是否国内外网站。
  • 广告过滤。同上。
  • 内网全千兆访问。访问 NAS 资源、各设备之间文件互传速度大幅提升,堪比本地访问。
  • Docker。开发时如果需要容器资源,内外网都可使用路由器的 Docker,节约开发机资源,目前资源是 4C8G。
  • 数据安全。之前 NAS 是直接暴露在公网的,且没有开启 HTTP,现在公网访问内网设备需要 VPN,访问 NAS 强制 HTTPS。
  • 扩大 WIFI 信号覆盖。未来搬去新家后智能设备增多,WIFI6 是必不可少的。出于成本考虑,WIFI6 AP 放在客厅,现有垃圾 AP 放在卧室。

硬件改造成本共 1135 元

  • 软路由: J4105,8G DDR4 + 64G SATA固态 咸鱼 750 元
  • 交换机: 水星SG108C 塑料8口 JD 69元
  • AP: 华为AX3Pro 咸鱼 295 元
  • 网线: 绿联6类线 0.5m x3 JD 21元

哭,现在设备各种涨价, ax3pro 暴涨 150 元,蹲了二周才蹲到低于 300 元的 AP,J4105 也涨了 100 多,不知道是不是受了这一波矿潮影响…

总之,想把网络环境做好,最大的变化就是没有变化,让用户感觉不到就成功了,只需要默默地提供稳定的服务就好~

阅读全文 »

Vite2 发布有一段时间了,其内部使用了 ESBuild 进行打包,ESBuild 有着相对 Webpack 惊人的打包速度。

esbuild benchmark 数据来自 github.com/evanw/esbuild

React 用户表示令人羡慕,不过 Vite 野心比较大,不止可以用来开发 Vue 项目,类似 Rollup,Vite2 可以使用插件来支持 React 项目。

这就巧了,手上刚好有一个 React 练习项目,使用 Create React App 创建并且进行过 Eject。接下来我们就用这个 React 项目来练练手吧,享受一下飞一般的构建速度!

阅读全文 »

变量

字符串

字符串可以用单引号,也可以用双引号,也可以不用引号。

单引号

单引号里任何字符都会原样输出,变量也是无效的,也无法对单引号进行转义。

1
str='this is a string'

双引号和字符串的拼接

双引号就可以随意的进行转义啦!

1
2
3
4
5
name="mutoe"
greeting="hello, $name !" # => hello, mutoe !
greeting="hello, "$name" !" # => hello, mutoe !
greeting="hello, ${name}!" # => hello, mutoe!
quote_greeting="hello, \"$name\" !" # => hello, "mutoe" !

获取字符串长度

1
2
str='abcd'
echo ${#abcd} # => 4

获取子字符串

1
2
str='abcdefg'
echo ${string:1:4} # => bcde

分割字符串

1
2
3
4
5
6
str="a,b,c,d"
arr=$(echo $str | tr ";" "\n")
for item in $arr
do
echo $item
done

数组

数组使用小括号定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 声明数组
array=(1 2 a b "foo")
array[10]=bar

# 读取数组
echo ${array[3]} # => b
echo ${array[9]} # =>
echo ${array[10]} # => bar
echo ${array[*]} # => 1 2 a b foo bar
echo ${array[@]} # => 1 2 a b foo bar

# 读取数组长度
echo ${#array[*]} # => 6

# 读取字符串元素长度
echo ${#array[10]} # => 3

# 删除元素
unset array[1]
echo ${array[*]} # => 1 a b foo bar
echo ${#array[*]} # => 5

# 拼接数组
new_arr=(0 ${array[*]} z)
echo ${new_arr[*]} # => 0 1 a b foo bar z
echo ${new_arr[6]} # => z

# 删除数组
unset new_arr
echo $new_arr # =>

关联数组

Bash shell 中可以使用字符串作为数组的下标,类似 Map 对象

1
2
3
4
5
6
declare -A color
color["red"]="#ff0000"
color["green"]="#00ff00"
color["blue"]="#0000ff"

echo $color

条件

if

1
2
3
4
5
6
7
8
9
if condition
then
...
elif condition2
then
...
else
...
fi

实际上,if 检测的是一条命令的退出状态。

Example
1
2
3
4
5
a=$[2*3]
b=$[1+5]
if [ $a == $b ]; then
echo "a == b"
fi

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case $var in
1)
command1
command2
;; # break (required)
$const|2) # or
command1
command2
;;
*) # default
command1
command2
;;
esac

注释

# 开始的部分就是注释, sh里没有多行注释,只能每行加一个 #

参考资料

第二章我们简单的介绍了下如何连接测试数据库,这篇我们将结合 TDD 来完整的实现注册和登录功能。(如果接下来我写的内容你已经做过,可以跳过该步)

1. 编写第一个集成测试

按照 Nest 提供的示例 E2E 测试, 位于 test/app.e2e-spec.ts 应该长这样

test/app.e2e-spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Test, TestingModule } from '@nestjs/testing'
import * as request from 'supertest'
import { AppModule } from './../src/app.module'

describe('AppController (e2e)', () => {
let app

beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile()

app = moduleFixture.createNestApplication()
await app.init()
})

it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!')
})
})

现在是运行不了的,不过没关系,我们稍稍改造一下

首先我们测试一个用于检查健康的接口 /hello, 无需导入整个 AppModule, 只需导入 AppController ,改造为下列的样子

app.e2e-spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { Test, TestingModule } from '@nestjs/testing'
import { AppController } from 'app.controller'
import * as request from 'supertest'

describe('app module', () => {
let app

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
controllers: [AppController],
}).compile()

app = moduleFixture.createNestApplication()
await app.init()
})

afterAll(async () => {
await app.close()
})

it('/hello (GET)', () => {
return request(app.getHttpServer())
.get('/hello?name=world')
.expect(200)
.expect('Hello world!')
})
})

然后执行

1
yarn test:e2e

yarn e2e test

阅读全文 »

这一节, 我们引入 Swagger 来自动根据代码里的注脚来生成接口文档。

Nest 为我们提供了一个专用的模块来搭配 Swagger 来使用

1. 安装依赖

1
yarn add @nestjs/swagger swagger-ui-express

2. 初始化 Swagger 模块

在我们的应用入口文件 main.ts 中添加一个 createSwagger 方法, 并在 bootstrap 方法中初始化它

main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { INestApplication, ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
import { AppModule } from 'app.module'

function createSwagger(app: INestApplication) {
const version = require('../package.json').version || ''

const options = new DocumentBuilder()
.setTitle('Nestjs Realworld Example App')
.setVersion(version)
.addBearerAuth()
.build()

const document = SwaggerModule.createDocument(app, options)
SwaggerModule.setup('/docs', app, document)
}

async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe())

if (process.env.SWAGGER_ENABLE && process.env.SWAGGER_ENABLE === 'true') {
createSwagger(app)
}

await app.listen(3000)
}

bootstrap().catch((err) => console.error(err))

createSwagger 中, 我们首先读取了来自 package.json 中的版本号来作为接口的版本

然后我们设置了 Title 和 Bearer 鉴权认证入口, 我们还设置了 /docs 为我们文档的入口

最后,我们判断环境变量中的 SWAGGER_ENABLE 是否打开, 如果打开我们就初始化 Swagger 文档系统。

.env.env.template 中增加 SWAGGER_ENABLE=true, 然后启动服务器

访问 http://localhost:3000/docs 就能看见我们的接口文档创建好啦!

swagger preview

阅读全文 »

这里记录了一些个人学习 Flutter 时遇到的一些问题, 可以作为避免踩坑和速查手册. 如有疑问欢迎留言.

阅读全文 »

上一章中, 我们完成了鉴权功能, 也就是 Auth 模块, 顺便简单实现了下注册和登录功能. 这一章我们就来正式的将登录和实现功能做完, 含有完整的数据校验和转化.

在正式的开始完善功能之前, 我们现清理重构一下已有的代码.

1. 重构代码

1.1 整理路由

我们上一张的登录和注册是写在 app.controller.ts 中的, 如果所有的路由都写在这里的话就会比较杂乱, 所以我们将它移动到 Auth 模块下.

首先生成 AuthController

1
nest g controller auth

然后将 /auth/register/auth/login 移到 AuthController 下

auth.controller.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { Body, Controller, Post, Request, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'
import { UserService } from '../user/user.service'
import { AuthService } from './auth.service'

@Controller('auth')
export class AuthController {
constructor(
private readonly userService: UserService,
private readonly authService: AuthService,
) {}

@Post('/register')
async register(
@Body() requestBody: { email: string; username: string; password: string },
) {
const user = await this.userService.createUser(requestBody)
const token = this.authService.generateToken(user.id, user.username)
return {
user: { ...user, token },
}
}

@UseGuards(AuthGuard('local'))
@Post('/login')
async login(@Request() req) {
const { user } = req
const token = this.authService.generateToken(user.id, user.username)
return {
user: { ...user, token },
}
}
}

顺便也移动下测试代码, 这里就不再赘述了.

阅读全文 »

上一章我们创建了一个用户表, 但是还没有实现真正的注册和登录. 要实现注册登录以及后续的权限校验, 我们还有一些工作要做.

目前有比较多的思路来对用户进行鉴权, 我们选用 Conduit 示例中展示的也是现在比较广泛的做法 JWT 进行认证.

要实现 JWT 鉴权, NestJS 为我们做好了大部分工作.

1. 安装依赖

但是在这之前, 我们要先安装下面的依赖

1
2
yarn add @nestjs/passport passport passport-local
yarn add -D @types/passport-local

Passport 你可以把它看作是一个小型的框架, 因为你可以通过一些简单的回调函数来进行配置. Passport 会在适当的时候对其进行调用.

@nestjs/passport 则对 Passport 进行了很好的集成.

阅读全文 »

上一个文章我们介绍了如何搭建一个开发环境和 Pipeline, 这篇文章开始我们将正式的用 TDD 的模式实现一个后端项目.

1. 安装依赖

我们选用了 Postgres 作为我们的数据库, 操作数据库的 ORM 我们选用 TypeORM, 这是一个 TypeScript 友好的 ORM, 并且 nest 也提供了非常便利的集成方法.

1
yarn add @nestjs/typeorm typeorm pg

2. 接入 TypeORM

编辑 app.module.ts

app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { TypeOrmModule } from '@nestjs/typeorm'

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'realworld',
password: '123456',
database: 'nestjs',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
阅读全文 »
0%