测试go test

news2024/11/17 5:18:25

目录

  • go test工具
  • 单元测试
    • 测试代码
    • go test -run
    • 跳过某些测试用例
    • 子测试
    • 表格驱动测试
      • 并行测试
    • 报告方法
    • 测试覆盖率
  • 基准测试
    • demo
    • 性能比较函数
    • 计时方法
    • 并行测试
    • TestMain
    • Setup与Teardown
  • httptest
    • 简单的 Web 应用
    • 测试

Go 语言从开发初期就注意了测试用例的编写。特别是静态语言,由于调试没有动态语言那么方便,所以能最快最方便地编写一个测试用例就显得非常重要了。

  1. testing 方便进行 Go 包的自动化单元测试、基准测试
  2. net/http/httptest 提供测试 HTTP 的工具

go test工具

Go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。

go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。

在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。

类型格式作用
测试函数函数名前缀为Test测试程序的一些逻辑行为是否正确
基准函数函数名前缀为Benchmark测试函数的性能
示例函数函数名前缀为Example为文档提供示例文档

go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

单元测试

格式:Xxx 可以是任何字母数字字符串,但是第一个字母不能是小写字母。

func TestXxxx(t *testing.T){
    // ...
}

t参数拥有以下方法:

func (c *T) Cleanup(func())
func (c *T) Error(args ...interface{})
func (c *T) Errorf(format string, args ...interface{})
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...interface{})
func (c *T) Fatalf(format string, args ...interface{})
func (c *T) Helper()
func (c *T) Log(args ...interface{})
func (c *T) Logf(format string, args ...interface{})
func (c *T) Name() string
func (c *T) Skip(args ...interface{})
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...interface{})
func (c *T) Skipped() bool
func (c *T) TempDir() string

gotest 的变量有这些:
test.short : 一个快速测试的标记,在测试用例中可以使用 testing.Short() 来绕开一些测试
test.outputdir : 输出目录
test.coverprofile : 测试覆盖率参数,指定输出文件
test.run : 指定正则来运行某个 / 某些测试用例
test.memprofile : 内存分析参数,指定输出文件
test.memprofilerate : 内存分析参数,内存分析的抽样率
test.cpuprofile : cpu 分析输出参数,为空则不做 cpu 分析
test.blockprofile : 阻塞事件的分析参数,指定输出文件
test.blockprofilerate : 阻塞事件的分析参数,指定抽样频率
test.timeout : 超时时间
test.cpu : 指定 cpu 数量
test.parallel : 指定运行测试用例的并行数

测试代码

// main.go
func Fib(n int) int {
        if n < 2 {
                return n
        }
        return Fib(n-1) + Fib(n-2)
}

// fib_test.go
func TestFib(t *testing.T) {
    var (
        in       = 7
        expected = 13
    )
    actual := Fib(in)
    if actual != expected {
        t.Errorf("Fib(%d) = %d; expected %d", in, actual, expected)
    }
}

执行 go test .,(可以为go test命令添加-v参数,让它输出完整的测试结果。)输出:
在这里插入图片描述
在这里插入图片描述

go test -run

  1. 在执行go test命令的时候可以添加-run参数,它对应一个正则表达式,只有函数名匹配上的测试函数才会被go test命令执行。

  2. 例如通过给go test添加-run=Sep参数来告诉它本次测试只运行TestFuckingSep这个测试用例。

跳过某些测试用例

为了节省时间支持在单元测试时跳过某些耗时的测试用例。

func TestTimeConsuming(t *testing.T) {
    if testing.Short() {
        t.Skip("short模式下会跳过该测试用例")
    }
    ...
}

当执行go test -short时就不会执行上面的TestTimeConsuming测试用例。

子测试

Go1.7+中新增了子测试,支持在测试函数中使用t.Run执行一组测试用例,这样就不需要为不同的测试数据定义多个测试函数了。

func TestXXX(t *testing.T){
  t.Run("case1", func(t *testing.T){...})
  t.Run("case2", func(t *testing.T){...})
  t.Run("case3", func(t *testing.T){...})
}
// 循环实现
func TestSplit(t *testing.T) {
	type test struct { // 定义test结构体
		input string
		sep   string
		want  []string
	}
	tests := map[string]test{ // 测试用例使用map存储
		"simple":      {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
		"wrong sep":   {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
		"more sep":    {input: "abcd", sep: "bc", want: []string{"a", "d"}},
		"leading sep": {input: "沙河有沙又有河", sep: "沙", want: []string{"河有", "又有河"}},
	}
	for name, tc := range tests {
		got := Split(tc.input, tc.sep)
		if !reflect.DeepEqual(got, tc.want) {
			t.Errorf("name:%s expected:%#v, got:%#v", name, tc.want, got) // 将测试用例的name格式化输出
		}
	}
}

// 用自测试方法实现
func TestSplit(t *testing.T) {
	type test struct { // 定义test结构体
		input string
		sep   string
		want  []string
	}
	tests := map[string]test{ // 测试用例使用map存储
		"simple":      {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
		"wrong sep":   {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
		"more sep":    {input: "abcd", sep: "bc", want: []string{"a", "d"}},
		"leading sep": {input: "沙河有沙又有河", sep: "沙", want: []string{"河有", "又有河"}},
	}
	for name, tc := range tests {
		t.Run(name, func(t *testing.T) { // 使用t.Run()执行子测试
			got := Split(tc.input, tc.sep)
			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("expected:%#v, got:%#v", tc.want, got)
			}
		})
	}
}

表格驱动测试

测试讲究 case 覆盖,按上面的方式,要覆盖更多 case 时,显然通过修改代码的方式很笨拙。这时可以采用 Table-Driven 的方式写测试,标准库中有很多测试是使用这种方式写的。

表格驱动测试的步骤通常是定义一个测试用例表格,然后遍历表格,并使用t.Run对每个条目执行必要的测试。

func TestFib(t *testing.T) {
    var fibTests = []struct {
        in       int // input
        expected int // expected result
    }{
        {1, 1},
        {2, 1},
        {3, 2},
        {4, 3},
        {5, 5},
        {6, 8},
        {7, 13},
    }

    for _, tt := range fibTests {
        actual := Fib(tt.in)
        if actual != tt.expected {
            t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
        }
    }
}

并行测试

表格驱动测试中通常会定义比较多的测试用例,而Go语言又天生支持并发,所以很容易发挥自身并发优势将表格驱动测试并行化。 想要在单元测试过程中使用并行测试,可以像下面的代码示例中那样通过添加t.Parallel()来实现。

func TestSplitAll(t *testing.T) {
	// 将 TLog 标记为能够与其他测试并行运行
	t.Parallel()  

	// 定义测试表格
	// 为每个测试用例设置了一个名称
	tests := []struct {
		name  string
		input string
		sep   string
		want  []string
	}{
		{"base case", "a:b:c", ":", []string{"a", "b", "c"}},
		{"wrong sep", "a:b:c", ",", []string{"a:b:c"}},
		{"more sep", "abcd", "bc", []string{"a", "d"}},
		{"leading sep", "沙河有沙又有河", "沙", []string{"", "河有", "又有河"}},
	}
	// 遍历测试用例
	for _, tt := range tests {
		tt := tt  // 注意这里重新声明tt变量(避免多个goroutine中使用了相同的变量)
		t.Run(tt.name, func(t *testing.T) { // 使用t.Run()执行子测试
			t.Parallel()  // 将每个测试用例标记为能够彼此并行运行
			got := Split(tt.input, tt.sep)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("expected:%#v, got:%#v", tt.want, got)
			}
		})
	}
}

报告方法

  1. 遇到一个断言错误的时候,标识这个测试失败:

    Fail() : 测试失败,测试继续
    FailNow() : 测试失败,测试中断
    
  2. 遇到一个断言错误,只希望跳过这个错误,但是不希望标识测试失败:

    SkipNow() : 跳过测试,测试中断
    
  3. 只希望打印信息

    Log : 输出信息
    Logf : 输出格式化的信息
    
  4. 希望跳过这个测试,并且打印出信息

    Skip : 相当于 Log + SkipNow
    Skipf : 相当于 Logf + SkipNow
    
  5. 希望断言失败的时候,标识测试失败,并打印出必要的信息,但是测试继续

    Error : 相当于 Log + Fail
    Errorf : 相当于 Logf + Fail
    
  6. 希望断言失败的时候,标识测试失败,打印出必要的信息,但中断测试

    Fatal : 相当于 Log + FailNow
    Fatalf : 相当于 Logf + FailNow
    

测试覆盖率

测试覆盖率是指代码被测试套件覆盖的百分比,也就是在测试中至少被运行一次的代码占总代码的比例。在公司内部一般会要求测试覆盖率达到80%左右。

使用go test -cover来查看测试覆盖率。

go test -cover
PASS
coverage: 100.0% of statements
ok      golang-unit-test-demo/base_demo 0.009s

Go还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件:

go test -cover -coverprofile=c.out
PASS
coverage: 100.0% of statements
ok      golang-unit-test-demo/base_demo 0.009s

然后执行go tool cover -html=c.out,使用cover工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。
在这里插入图片描述

基准测试

基准测试就是在一定的工作负载之下检测程序性能的一种方法。

func BenchmarkName(b *testing.B){
    // ...
}

基准测试以Benchmark为前缀,需要一个*testing.B类型的参数b,基准测试必须要执行b.N次,这样的测试才有对照性,b.N的值是系统根据实际情况去调整的,从而保证测试的稳定性。 testing.B拥有的方法如下:

func (c *B) Error(args ...interface{})
func (c *B) Errorf(format string, args ...interface{})
func (c *B) Fail()
func (c *B) FailNow()
func (c *B) Failed() bool
func (c *B) Fatal(args ...interface{})
func (c *B) Fatalf(format string, args ...interface{})
func (c *B) Log(args ...interface{})
func (c *B) Logf(format string, args ...interface{})
func (c *B) Name() string
func (b *B) ReportAllocs()
func (b *B) ResetTimer()
func (b *B) Run(name string, f func(b *B)) bool
func (b *B) RunParallel(body func(*PB))
func (b *B) SetBytes(n int64)
func (b *B) SetParallelism(p int)
func (c *B) Skip(args ...interface{})
func (c *B) SkipNow()
func (c *B) Skipf(format string, args ...interface{})
func (c *B) Skipped() bool
func (b *B) StartTimer()
func (b *B) StopTimer()

demo

func BenchmarkHello(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Sprintf("hello")
    }
}

通过 go test 命令,加上 -bench 标志来执行。

结果输出:
在这里插入图片描述

  1. 意味着函数执行了 30542277 次,每次循环花费 33.73 纳秒 (ns)。
  2. 数字16表示GOMAXPROCS的值,这个对于并发基准测试很重要.

还可以为基准测试添加-benchmem参数,来获得内存分配的统计数据。

在这里插入图片描述
5 B/op表示每次操作内存分配了5字节,1 allocs/op则表示每次操作进行了1次内存分配

性能比较函数

通常需要对两个不同算法的实现使用相同的输入来进行基准比较测试。

func benchmarkFib(b *testing.B, n int) {
	for i := 0; i < b.N; i++ {
		Fib(n)
	}
}

func BenchmarkFib1(b *testing.B)  { benchmarkFib(b, 1) }
func BenchmarkFib2(b *testing.B)  { benchmarkFib(b, 2) }
func BenchmarkFib3(b *testing.B)  { benchmarkFib(b, 3) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }
func BenchmarkFib40(b *testing.B) { benchmarkFib(b, 40) }

默认情况下,每个基准测试至少运行1秒。如果在Benchmark函数返回时没有到1秒,则b.N的值会按1,2,5,10,20,50,…增加,并且函数再次运行。

可以使用-benchtime标志增加最小基准时间,以产生更准确的结果:
go test -bench=Fib40 -benchtime=20s

计时方法

有三个方法用于计时:

  1. StartTimer:开始对测试进行计时。该方法会在基准测试开始时自动被调用,也可以在调用 StopTimer 之后恢复计时;
  2. StopTimer:停止对测试进行计时。当需要执行一些复杂的初始化操作,并且不想对这些操作进行测量时,就可以使用这个方法来暂时地停止计时;
    3/ ResetTimer:对已经逝去的基准测试时间以及内存分配计数器进行清零。对于正在运行中的计时器,这个方法不会产生任何效果。
func BenchmarkSplit(b *testing.B) {
	time.Sleep(5 * time.Second) // 假设需要做一些耗时的无关操作
	b.ResetTimer()              // 重置计时器
	for i := 0; i < b.N; i++ {
		Split("xx.xx.xx.xx", ".")
	}
}

并行测试

func (b *B) RunParallel(body func(*PB))会以并行的方式执行给定的基准测试。

通过 RunParallel 方法能够并行地执行给定的基准测试。RunParallel会创建出多个 goroutine,并将 b.N 分配给这些 goroutine 执行,其中 goroutine 数量的默认值为 GOMAXPROCS。用户如果想要增加非 CPU 受限(non-CPU-bound)基准测试的并行性,那么可以在 RunParallel 之前调用 SetParallelism(如 SetParallelism(2),则 goroutine 数量为 2*GOMAXPROCS)。RunParallel 通常会与 -cpu 标志一同使用。

body 函数将在每个 goroutine 中执行,这个函数需要设置所有 goroutine 本地的状态,并迭代直到 pb.Next 返回 false 值为止。因为 StartTimer、StopTime 和 ResetTimer 这三个方法都带有全局作用,所以 body 函数不应该调用这些方法; 除此之外,body 函数也不应该调用 Run 方法。

func BenchmarkSplitParallel(b *testing.B) {
	// b.SetParallelism(1) // 设置使用的CPU数
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			Split("xx.xx.xx.xx", ".")
		}
	})
}

还可以通过在测试命令后添加-cpu参数如go test -bench=. -cpu 1来指定使用的CPU数量。

TestMain

通过在*_test.go文件中定义TestMain函数来可以在测试之前进行额外的设置(setup)或在测试之后进行拆卸(teardown)操作。

如果测试文件包含函数:func TestMain(m *testing.M)那么生成的测试会先调用 TestMain(m),然后再运行具体测试。TestMain运行在主goroutine中, 可以在调用 m.Run前后做任何设置(setup)和拆卸(teardown)。退出测试的时候应该使用m.Run的返回值作为参数调用os.Exit。

func TestMain(m *testing.M) {
	fmt.Println("write setup code here...") // 测试之前的做一些设置
	// 如果 TestMain 使用了 flags,这里应该加上flag.Parse()
	retCode := m.Run()                         // 执行测试
	fmt.Println("write teardown code here...") // 测试之后做一些拆卸工作
	os.Exit(retCode)                           // 退出测试
}

Setup与Teardown

有时候可能需要为每个测试集设置Setup与Teardown,也有可能需要为每个子测试设置Setup与Teardown。

// 测试集的Setup与Teardown
func setupTestCase(t *testing.T) func(t *testing.T) {
	t.Log("如有需要在此执行:测试之前的setup")
	// 返回Teardown
	return func(t *testing.T) {
		t.Log("如有需要在此执行:测试之后的teardown")
	}
}

// 子测试的Setup与Teardown
func setupSubTest(t *testing.T) func(t *testing.T) {
	t.Log("如有需要在此执行:子测试之前的setup")
	// 返回Teardown
	return func(t *testing.T) {
		t.Log("如有需要在此执行:子测试之后的teardown")
	}
}
func TestSplit(t *testing.T) {
	type test struct { // 定义test结构体
		input string
		sep   string
		want  []string
	}
	tests := map[string]test{ // 测试用例使用map存储
		"simple":      {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
		"wrong sep":   {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
		"more sep":    {input: "abcd", sep: "bc", want: []string{"a", "d"}},
		"leading sep": {input: "沙河有沙又有河", sep: "沙", want: []string{"", "河有", "又有河"}},
	}
	teardownTestCase := setupTestCase(t) // 测试之前执行setup操作
	defer teardownTestCase(t)            // 测试之后执行testdoen操作

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) { // 使用t.Run()执行子测试
			teardownSubTest := setupSubTest(t) // 子测试之前执行setup操作
			defer teardownSubTest(t)           // 测试之后执行testdoen操作
			got := Split(tc.input, tc.sep)
			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("expected:%#v, got:%#v", tc.want, got)
			}
		})
	}
}

httptest

由于 Go 标准库的强大支持,Go 可以很容易的进行 Web 开发。为此,Go 标准库专门提供了 net/http/httptest 包专门用于进行 http Web 开发测试。

简单的 Web 应用

// 保存 Topic,没有考虑并发问题
var TopicCache = make([]*Topic, 0, 16)

type Topic struct {
    Id        int       `json:"id"`
    Title     string    `json:"title"`
    Content   string    `json:"content"`
    CreatedAt time.Time `json:"created_at"`
}

func main() {
    http.HandleFunc("/topic/", handleRequest)
    http.ListenAndServe(":2017", nil)
}
/topic/ 开头的请求都交由 handleRequest 处理,它根据不同的 Method 执行相应的增删改查
...

测试

func TestHandlePost(t *testing.T) {
    mux := http.NewServeMux()
    mux.HandleFunc("/topic/", handleRequest)

    reader := strings.NewReader(`{"title":"The Go Standard Library","content":"It contains many packages."}`)
    r, _ := http.NewRequest(http.MethodPost, "/topic/", reader)
	
	// httptest.NewRecorder() 可以获得 httptest.ResponseRecorder 结构,而此结构实现了http.ResponseWriter 接口。
    w := httptest.NewRecorder()

    mux.ServeHTTP(w, r)

    resp := w.Result()
    if resp.StatusCode != http.StatusOK {
        t.Errorf("Response code is %v", resp.StatusCode)
    }
}

通过 go test -v 运行测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java入门系列六】java基础-面向对象进阶**

学习记录&#x1f914;IDE快捷键包访问修饰符封装结合构造器进行封装继承-代码的复用性继承的构造器继承的本质super关键字方法的重写override多态**难点多态trickinstanceof动态绑定**多态数组多态参数object类trickObject类创建对象流程讨论总结谢谢点赞交流&#xff01;(❁◡…

MDK的格式化代码工具及添加快捷方式

文章目录Astyle介绍插件安装参数设置格式化整个工程参数说明快捷键设置参考Astyle介绍 Astyle 即Artistic Style&#xff0c;是一个可用于C, C, C/CLI, Objective‑C, C# 和Java编程语言格式化开源工具。 官网&#xff1a;Artistic Style - Index 插件安装 不用安装&#xf…

年前花2个月面过阿里测开岗,拿个27K也不过分吧?

背景介绍 美本计算机专业&#xff0c;代码能力一般&#xff0c;之前有过两段实习以及一个学校项目经历。第一份实习是大二暑期在深圳的一家互联网公司做前端开发&#xff0c;第二份实习由于大三暑假回国的时间比较短&#xff08;小于两个月&#xff09;&#xff0c;于是找的实…

23.1.29 make menuconfig执行过程

1、将uboot源码拷贝ubuntu中 1.在家目录创建一 -个demo文件夹 2.将en. SOURCES - stm32mp1- openstlinux-5.10- dunfell- mp1-21- 11-17_ tar_ v3.1.0. xz文件夹拷贝到~/ demo文件夹下面 3.进入~/demo目录下&#xff0c;对en . SOURCES- stm32mp1 - openstlinux-5. 10- dunfell…

第02天-Java数据结构和算法

目录 021_单链表新浪面试题 单链表面试题&#xff08;新浪、百度、腾讯&#xff09; 代码实现 022_单链表腾讯面试题 图解 代码实现 023_单链表百度面试题 图解 代码实现 024_双向链表增删改查分析图解 双向链表应用实例 图解 代码实现 026_双向链表功能测试和小结 …

10个方法教你解决虚幻4运行崩溃问题

“多年来我一直在我的电脑上使用不同版本的虚幻引擎 4&#xff0c;但最近它突然在启动时崩溃。我最初认为这是一个项目相关的问题&#xff0c;但后来注意到即使是从桌面图标或 Epic Games Launcher执行Unreal Engine 4&#xff0c;也是闪了几秒就直接闪退了。这个问题真是让我很…

Nginx从入门到精通(从安装到实践,持续更新中)

一&#xff0c;安装从官网下载相应的tar包http://nginx.org/通过命令tar zxvf tar包名 -c 解压路径 解压到指定的目录下在解压的nginx包下有一个confuture文件&#xff0c;可通过./confuture判断是否符合安装条件&#xff08;./configure --prefix/usr/local/nginx即可指定一会…

H3CMSR 系列路由器限速配置

1 配置需求或说明 1.1 适用产品系列 本手册适用于如下产品&#xff1a;MSR 全系列路由器 1.2 配置需求及实现的效果 MSR路由器G0/0接口连接公网&#xff0c;G0/1接口连接内网&#xff0c;内网网关地址为MSR路由器VLAN1虚接口地址192.168.1.1/24&#xff0c;需要实现对内网I…

1605_Git版本管理概念图解_git_for_computer_scientists阅读

全部学习汇总&#xff1a; GitHub - GreyZhang/g_unix: some basic learning about unix operating system. 目前没有很好的笔记分类放置这一份学习笔记&#xff0c;因为我的工具箱分类并不适合它。我之前的工具箱笔记主要还是简洁扼要来列出工具使用的参考&#xff0c;而这个其…

超导量子计算机

1.超导量子计算机发展状况 2018年3月5日美国物理学会年会上&#xff0c;谷歌展示了其正在测试的72量子位超导量子芯片Bristlecone。谷歌物理学家朱利安凯利表示&#xff0c;研讨团队希望初次运用更大的量子芯片来展现霸权&#xff0c;并完成传统计算机不能够完成的计算。芯片之…

React学习教程

React学习教程git地址React基础知识点1.什么是React &#xff08;★★★&#xff09;特点2.React脚手架2.1 使用React脚手架初始化项目2.2 项目目录说明调整JSX基础1. JSX介绍2. JSX中使用js表达式3. JSX列表渲染4. JSX条件渲染5. JSX样式处理6. JSX注意事项React组件1.React组…

监控系统的基本架构(Metric monitoring)

前言 最近准备做一个监控系统&#xff0c;正好看到了这篇文章&#xff0c;这篇文章很简单&#xff0c;但很清晰&#xff0c;结合原文的图片&#xff0c;我进行一下翻译。 原文地址 ByteByteGo 原文 A well-designed metric monitoring and alerting system plays a key rol…

YoLoV1~YoLoV3 SPP

截止到今日&#xff0c;差不多对深度学习有了一定了解了&#xff0c;从图像分类的各种神经网络再到YOLO系列的目标检测&#xff0c;一步一步的逐渐实现相应功能&#xff0c;但对于一些具体的代码细节、部分理论&#xff0c;后期可能还需要加强学习和理解。但是转眼也快开学了&a…

IB 课程的挑战 (一)

近年来许多学校都引入 IB 课程 (国际预科文凭课程)&#xff0c;让家长在为子女安排升学路途上能有更多的选择。然而&#xff0c;学生在修读IB课程时会遇到什么挑战呢?以下我们就会为大家分享几个有关IB课程的挑战&#xff0c;让各位家长得以参考。 挑战一&#xff1a;时间分配…

ConstraintLayout 使用详解,减少嵌套 UI, 提升性能

前言 对于初学者来说&#xff0c;可能觉得ConstraintLayout属性多&#xff0c;且属性长而弃用它&#xff0c;那你错失了这个大宝贝。 因为在复杂布局&#xff0c;我们会一直用RelativeLayout和LinearLayout去嵌套&#xff0c;因为嵌套的ViewGroup会导致手机多次测量和绘制&am…

解剖华为 Mate 50 Pro主板

华为Mate 50 Pro整体拆解难度中等&#xff0c;可还原性强。主板则是采用堆叠结构&#xff0c;主板1正面主要IC包括高通骁龙84G处理器芯片…… 日前&#xff0c;有拆解机构对华为Mate 50 Pro整机进行了拆解&#xff0c;表示其内部的配件大约有90%是国产元器件&#xff0c;如屏幕…

【Node.js实战】一文带你开发博客项目之Express重构(初始化环境,处理 session,连接 redis)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

循环UI列表

先看一下效果 支持自定义选项数量,按钮切换,鼠标滑动切换,当前项框选提示,选项缩放等功能 SlideSwitch&#xff1a;鼠标切换选项开关,关闭只能点击按钮切换 SlideOffset&#xff1a;滑动触发值,鼠标X轴向滑动大于此值切换选项,小于不触发 ScaleSwitch&#xff1a;缩放开关,开启…

乘法逆元 +数论分块 +平方和公式

年后准备学习啦&#xff0c;开学还得准备考试。 乘法逆元&#xff1a; 因为涉及到除法&#xff0c;所以取余这个操作就错误。 所以如果我们要求&#xff08;a/b)%mod&#xff0c;我们可以假设 (a/b)%mod a*c%mod 那么c就是b的逆元。 怎么求逆元呢&#xff0c;其实有很多方法…

指定加拿大UBC|临床肿瘤专业应届博士成功获访问学者offer

G博士指定加拿大UBC&#xff0c;本人具有多年的临床工作经验&#xff0c;但科研产出较少。经过努力&#xff0c;最终我们落实了该校的访问学者职位。又历经半年的流程&#xff0c;G博士终于获得加拿大签证&#xff0c;前往UBC报到。建议&#xff1a;提前申请&#xff0c;预留出…