字节跳动青训营Day02 - 并发编程,单元测试

简介: 1.单元测试初步讲解Go 语言推荐测试文件和源代码文件放在同一目录下,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数,那么应该新建 calc_test.go 作为测试文件


1.单元测试初步讲解


Go 语言推荐测试文件和源代码文件放在同一目录下,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数,那么应该新建 calc_test.go 作为测试文件


那么在Goland IDE中,我们一般采用如下方法做单元测试:


首先,单元测试的目录结构:



我们选中两个目标(源代码+单元测试test),右键做如下选择:



之后将会生成单元测试的结果


2.并发


协程 Goroutine


Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。


goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。


goroutine 语法格式:


go 函数名( 参数列表 )


协程演示:


package concurrence
import (
  "fmt"
  "time"
)
// 函数
func hello(i int) {
  println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine() {
  for i := 0; i < 5; i++ {
    // 开启一个新的协程
    go func(j int) {
      hello(j)
    }(i)
  }
  // 需要等待片刻
  time.Sleep(time.Second)
}


单元测试:


package concurrence
import (
  "testing"
)
func TestManyGo(t *testing.T) {
  HelloGoRoutine()
}


输出:他们是两个进程在工作,显然是没有规律的:


hello goroutine : 4
hello goroutine : 3
hello goroutine : 0
hello goroutine : 2
hello goroutine : 1


通道 Channel


通道(channel)是用来传递数据的一个数据结构

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:


ch := make(chan int)


通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小


ch := make(chan int, 100)


带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

通道演示,计算0–9的平方:


package concurrence
func CalSquare() {
  // 声明两个通道
  src := make(chan int)
  // 声明一个带缓冲区的通道
  dest := make(chan int, 3)
  go func() {
    defer close(src)
    for i := 0; i < 10; i++ {
      // 将i一个一个传到src通道里面
      src <- i
    }
  }()
  go func() {
    defer close(dest)
    // 一个一个接受src通道里的值,进入dest通道
    for i := range src {
      // 在dest通道中,计算i的平方
      dest <- i * i
    }
  }()
  // 输出dest通道里的所有值
  for i := range dest {
    //复杂操作
    println(i)
  }
}


单元测试:


package concurrence
import "testing"
func TestCalSquare(t *testing.T) {
  CalSquare()
}


测试输出:


0
1
4
9
16
25
36
49
64
81
---------------------
可以看到,是正确的输出结果


锁Lock和线程同步 WaitGroup


介绍了锁及线程同步的使用场景和方法:


package concurrence
import (
  "sync"
  "time"
)
var (
  x    int64
  lock sync.Mutex
)
func addWithLock() {
  for i := 0; i < 2000; i++ {
    lock.Lock()
    x += 1
    lock.Unlock()
  }
}
func addWithoutLock() {
  for i := 0; i < 2000; i++ {
    x += 1
  }
}
func Add() {
  x = 0
  for i := 0; i < 5; i++ {
    go addWithoutLock()
  }
  time.Sleep(time.Second)
  println("WithoutLock:", x)
  x = 0
  for i := 0; i < 5; i++ {
    go addWithLock()
  }
  time.Sleep(time.Second)
  println("WithLock:", x)
}
func ManyGoWait() {
  var wg sync.WaitGroup
  wg.Add(5)
  for i := 0; i < 5; i++ {
    go func(j int) {
      defer wg.Done()
      // 这里的hello是goroutine.go里的函数
      hello(j)
    }(i)
  }
  wg.Wait()
}


单元测试:


package concurrence
import "testing"
func TestAddLock(t *testing.T) {
  Add()
}
func TestManyGoWait(t *testing.T){
  ManyGoWait()
}


3.单元测试细讲


例子A


业务代码:主要的作用是返回一个字符串:


package test
func HelloTom() string {
  return "Tom"
}


单元测试,检测返回结果是否正确:


package test
import (
  "github.com/stretchr/testify/assert"
  "testing"
)
func TestHelloTom(t *testing.T) {
  output := HelloTom()
  expectOutput := "Tom"
  assert.Equal(t, expectOutput, output)
}


例子B


业务代码,主要是判断分数是否及格:


package test
func JudgePassLine(score int16) bool {
  if score >= 60 {
    return true
  }
  return false
}


单元测试:测试及格和不及格的预想输出:


package test
import (
  "github.com/stretchr/testify/assert"
  "testing"
)
func TestJudgePassLineTrue(t *testing.T) {
  isPass := JudgePassLine(70)
  assert.Equal(t, true, isPass)
}
func TestJudgePassLineFail(t *testing.T) {
  isPass := JudgePassLine(50)
  assert.Equal(t, false, isPass)
}


Mock 测试


业务代码,对文件内容的一些操作:


package test
import (
  "bufio"
  "os"
  "strings"
)
// 读文件内容
func ReadFirstLine() string {
  // 打开目录下的log文件
  open, err := os.Open("log")
  defer open.Close()
  if err != nil {
    return ""
  }
  // 读取文件,并返回
  scanner := bufio.NewScanner(open)
  for scanner.Scan() {
    return scanner.Text()
  }
  return ""
}
// 选出文件的首行内容
func ProcessFirstLine() string {
  line := ReadFirstLine()
  // 将文件中的11字符替换为00字符
  destLine := strings.ReplaceAll(line, "11", "00")
  return destLine
}


单元测试:


package test
import (
  "bou.ke/monkey"
  "github.com/stretchr/testify/assert"
  "testing"
)
func TestProcessFirstLine(t *testing.T) {
  firstLine := ProcessFirstLine()
  // 看一下是否成功替换了
  assert.Equal(t, "line00", firstLine)
}
func TestProcessFirstLineWithMock(t *testing.T) {
  monkey.Patch(ReadFirstLine, func() string {
    return "line110"
  })
  defer monkey.Unpatch(ReadFirstLine)
  line := ProcessFirstLine()
  assert.Equal(t, "line000", line)
}


基准测试


业务代码:随机数:


package benchmark
import (
  "github.com/bytedance/gopkg/lang/fastrand"
  "math/rand"
)
var ServerIndex [10]int
func InitServerIndex() {
  for i := 0; i < 10; i++ {
    ServerIndex[i] = i+100
  }
}
func Select() int {
  return ServerIndex[rand.Intn(10)]
}
func FastSelect() int {
  return ServerIndex[fastrand.Intn(10)]
}


基准测试,单元测试:


package benchmark
import (
  "testing"
)
func BenchmarkSelect(b *testing.B) {
  InitServerIndex()
  b.ResetTimer()
  for i := 0; i < b.N; i++ {
    Select()
  }
}
func BenchmarkSelectParallel(b *testing.B) {
  InitServerIndex()
  b.ResetTimer()
  b.RunParallel(func(pb *testing.PB) {
    for pb.Next() {
      Select()
    }
  })
}
func BenchmarkFastSelectParallel(b *testing.B) {
  InitServerIndex()
  b.ResetTimer()
  b.RunParallel(func(pb *testing.PB) {
    for pb.Next() {
      FastSelect()
    }
  })
}
目录
相关文章
|
8月前
|
IDE 测试技术 Go
【字节跳动青训营】后端笔记整理-3 | Go语言工程实践之测试
用于验证已经修改或新增功能后,软件的既有功能是否受到影响。
134 2
|
测试技术 Linux Go
后端实践--单元测试与性能优化 青训营
后端实践--单元测试与性能优化 青训营
|
存储 域名解析 负载均衡
【题解】结营小测试全解析:第六届字节后端青训营
小伙伴们!快来看看结营解析答案,验证一下自己是不是前百分之七十吧!这部分都是根据个人经验和网上收集所得到的第六届青训营结营全解析,如果存在错误,也请在评论区友好讨论,让我们一起把这篇文章变得更加完美。
274 0
|
测试技术 Go
怎么写Go基准测试 | 青训营笔记
怎么写Go基准测试 | 青训营笔记
102 0
|
测试技术 Go
Go语言工程实践之测试 | 青训营笔记
Go语言工程实践之测试 | 青训营笔记
97 0
|
Java
并发编程11-测试并发程序
<div class="markdown_views"> <p>测试的两个方面: <br> - 安全性 并发不会造成状态错误。 <br> - 活跃度 主要是性能测试 <br> - 吞吐量 段时间定资源内可以处理的任务数量 <br> - 响应性 从请求到完成一些动作之前的延迟(等待执行的时间) <br> - 可伸缩性 增加资源,提高性能</p> <
1369 0
|
16天前
|
监控 JavaScript 测试技术
postman接口测试工具详解
Postman是一个功能强大且易于使用的API测试工具。通过详细的介绍和实际示例,本文展示了Postman在API测试中的各种应用。无论是简单的请求发送,还是复杂的自动化测试和持续集成,Postman都提供了丰富的功能来满足用户的需求。希望本文能帮助您更好地理解和使用Postman,提高API测试的效率和质量。
66 11
|
2月前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
69 3
|
3月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
82 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
4月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
280 7
Jmeter实现WebSocket协议的接口测试方法