GO单元测试的使用

news/发布时间2024/9/20 7:52:21

Go语言中的测试依赖go test命令,go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。每个测试函数必须导入testing包,测试函数的名字必须以Test开头,必须接收一个*testing.T类型参数,在当前路径下执行go test命令即可执行测试函数。

场景一:网络测试

当我们编写代码时,有时我们需要测试的代码涉及到网络操作,可能是作为服务器端对外提供服务,也可能是依赖于其他网络服务(比如调用外部API接口)。在测试这些场景时,我们通常不希望真正建立网络连接,而是希望在测试过程中模拟网络的行为。接下来将重点介绍如何在这两种情况下进行网络的模拟测试。

首先是搭建一个http server端,对外提供HTTP服务。这是一个简单的Gin框架的HTTP服务器,主要包含了一个helloHandler函数,用于处理HTTP请求。SetupRouter 函数创建了一个路由,将 helloHandler 函数绑定到 "/hello" 路径上。

// 请求参数的结构体定义
type Param struct {Name string `json:"name"`
}// 处理HTTP请求的函数,返回字符串 "Hello, " + name
func helloHandler(c *gin.Context) {var p Param // 定义一个Param类型的变量p// 调用ShouldBindJSON解析JSON请求并绑定到Param结构体中if err := c.ShouldBindJSON(&p); err != nil {// 如果绑定失败,返回错误信息c.JSON(http.StatusOK, gin.H{"msg": "we need a name",})return}// 如果绑定成功,返回格式化字符串 "Hello, name"c.JSON(http.StatusOK, gin.H{"msg": fmt.Sprintf("hello %s", p.Name),})
}// 创建一个HTTP路由,并绑定到"/hello"路径的helloHandler函数上
func SetupRouter() *gin.Engine {router := gin.Default() // 创建一个默认的gin引擎router.POST("/hello", helloHandler) // 创建一个HTTP路由,使用helloHandler函数处理POST请求return router
}

在测试部分,我们使用了SetupRouter函数来初始化一个测试用的路由实例 r,然后遍历 tests 切片中的测试用例,每个测试用例调用r.ServeHTTP方法并将mock的请求和响应传入,进行单元测试。每个测试用例的结果都会在测试输出中显示。

func Test_helloHandler(t *testing.T) {// 定义两个测试用例tests := []struct {name   stringparam  stringexpect string}{{"base case", `{"name": "fengmi"}`, "hello fengmi"},{"bad case", "", "we need a name"},}r := SetupRouter()for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {// mock一个HTTP请求req := httptest.NewRequest("POST",                      // 请求方法"/hello",                    // 请求URLstrings.NewReader(tt.param), // 请求参数)// mock一个响应记录器w := httptest.NewRecorder()// 让server端处理mock请求并记录返回的响应内容r.ServeHTTP(w, req)// 校验状态码是否符合预期assert.Equal(t, http.StatusOK, w.Code)// 解析并检验响应内容是否复合预期var resp map[string]stringerr := json.Unmarshal([]byte(w.Body.String()), &resp)assert.Nil(t, err)assert.Equal(t, tt.expect, resp["msg"])})}
}

遍历了两个测试用例并在每个用例内执行了下列操作:

  1. 调用 httptest.NewRequest() 函数创建了一个 mock 的 HTTP 请求 req
  2. 调用 httptest.NewRecorder() 函数创建了一个响应记录器 w
  3. 使用 r.ServeHTTP(w, req) 函数将 mock 的请求和响应传入测试的路由实例中。
  4. 使用 assert.Equal() 函数检查 w.Code 是否符合预期的 http.StatusOK 状态码。
  5. 使用 assert.Equal() 函数检查 w.Body.String() 的内容是否与 expect 的内容相同。

如果上述所有的操作都执行成功,那么说明测试通过。


如果我们是在代码中请求外部API的场景(比如通过API调用其他服务获取返回值),该怎么编写单元测试呢?

例如,我们有以下业务逻辑代码,依赖外部API:http://your-api.com/post提供的数据。

// ReqParam API请求参数
type ReqParam struct {X int `json:"x"`
}// Result API返回结果
type Result struct {Value int `json:"value"`
}func GetResultByAPI(x, y int) int {p := &ReqParam{X: x}b, _ := json.Marshal(p)// 调用其他服务的APIresp, err := http.Post("http://your-api.com/post","application/json",bytes.NewBuffer(b),)if err != nil {return -1}body, _ := ioutil.ReadAll(resp.Body)var ret Resultif err := json.Unmarshal(body, &ret); err != nil {return -1}// 这里是对API返回的数据做一些逻辑处理return ret.Value + y
}

在对类似上述这类业务代码编写单元测试的时候,如果不想在测试过程中真正去发送请求或者依赖的外部接口还没有开发完成时,我们可以在单元测试中对依赖的API进行mock。这里推荐使用gock这个库。

func TestGetResultByAPI(t *testing.T) {defer gock.Off() // 首先用defer gock.Off()来清除已经存在的所有挂起的模拟请求,以免它们对我们测试的结果造成干扰。// mock 请求外部api时传参x=1返回100gock.New("http://your-api.com").Post("/post").MatchType("json").JSON(map[string]int{"x": 1}).Reply(200).JSON(map[string]int{"value": 100})// 调用我们的业务函数res := GetResultByAPI(1, 1)// 校验返回结果是否符合预期assert.Equal(t, res, 101)// mock 请求外部api时传参x=2返回200gock.New("http://your-api.com").Post("/post").MatchType("json").JSON(map[string]int{"x": 2}).Reply(200).JSON(map[string]int{"value": 200})// 调用我们的业务函数res = GetResultByAPI(2, 2)// 校验返回结果是否符合预期assert.Equal(t, res, 202)assert.True(t, gock.IsDone()) // 断言mock被触发
}

使用 gock 创建了一个 Mock 来模拟对 "http://your-api.com/post" 接口的请求。当您在本地请求这个接口时,即使请求真实的服务器("http://your-api.com"),由于使用了 gock 的 Mock,它会返回您预先设置的响应,而不会真正请求到该外部 API。


场景二:MySQL和Redis测试

 除了网络依赖之外,我们在开发中也会经常用到各种数据库,比如常见的MySQL和Redis等。接下来就分别举例来演示如何在编写单元测试的时候对MySQL和Redis进行mock。

sqlmock 是一个用于单元测试的 Mock 库,它实现了 sql/driver 接口,因此能够模拟任何 SQL 驱动程序的行为。它可以帮助您在测试中模拟数据库的行为,而无需连接到实际的数据库服务器。这使得编写单元测试时更加方便,也能够提高测试的效率。通过 sqlmock,您可以在测试中:

  • 设置期望的 SQL 查询和返回结果。
  • 模拟数据库连接失败或发生错误的情况。
  • 通过验证设置的期望来确保代码的行为与预期一致。

接下来测试的函数是recordStats,函数用来记录用户浏览商品时产生的相关数据,该函数的主要逻辑是:先开启一个事务,然后执行两条SQL语句,一条更新products表,一条向product_viewers表插入数据。如果执行过程中出现了错误,事务会被回滚,否则事务会提交。 

func recordStats(db *sql.DB, userID, productID int64) (err error) {// 开启事务// 操作views和product_viewers两张表tx, err := db.Begin()if err != nil {return}defer func() {switch err {case nil:err = tx.Commit()default:tx.Rollback()}}()// 更新products表if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {return}// product_viewers表中插入一条数据if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)",userID, productID); err != nil {return}return
}func main() {// 注意:测试的过程中并不需要真正的连接db, err := sql.Open("mysql", "root@/blog")if err != nil {panic(err)}defer db.Close()// userID为1的用户浏览了productID为5的产品if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {panic(err)}
}

对于sql执行成功的测试案例: 

'下面的代码是一个使用了Go标准库中testing模块的单元测试函数。它的主要功能是对recordStats函数的行为进行测试。

// TestShouldUpdateStats sql执行成功的测试用例
func TestShouldUpdateStats(t *testing.T) {// mock一个*sql.DB对象,不需要连接真实的数据库db, mock, err := sqlmock.New()if err != nil {t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)}defer db.Close()// mock执行指定SQL语句时的返回结果mock.ExpectBegin()mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectCommit()// 将mock的DB对象传入我们的函数中if err = recordStats(db, 2, 3); err != nil {t.Errorf("error was not expected while updating stats: %s", err)}// 确保期望的结果都满足if err := mock.ExpectationsWereMet(); err != nil {t.Errorf("there were unfulfilled expectations: %s", err)}
}
  1. 首先,在测试函数中使用sqlmock.New()创建了一个虚拟的数据库连接db和相应的mock对象。这个虚拟的数据库连接是在内存中创建的,并不需要真正连接到一个数据库服务器。

  2. 接着,在创建的mock对象上调用ExpectBegin()ExpectExec()ExpectCommit()方法,分别模拟了事务开始、UPDATE操作和INSERT操作的行为。

其原理是这样的:使用 mock.ExpectExec()方法设置一个预期的查询语句后,SQLMock 会将这个查询语句加入到一个内部的“预期查询队列”中。当测试代码中执行这个查询语句时,SQLMock 会在内部查询队列中寻找是否有匹配的预期查询操作。如果找到了一个匹配的预期查询操作,SQLMock 将返回你事先设置的结果;否则,SQLMock 将返回一个默认的空结果集。

具体来说:

  • ExpectBegin()用来模拟事务开始的行为。

  • ExpectExec("UPDATE products")表示我们期望执行一条SQL语句,其内容为UPDATE products。然后我们调用了WillReturnResult()方法,并传入了一个由sqlmock.NewResult(1, 1)创建的结果对象,表示这条SQL语句执行成功,并且最后插入的ID是1,影响的行数也是1。

  • ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3)表示我们期望执行一条SQL语句,其内容为INSERT INTO product_viewers,并且指定参数是(2, 3)。然后我们调用了WillReturnResult()方法,并传入了一个由sqlmock.NewResult(1, 1)创建的结果对象,表示这条SQL语句执行成功,并且最后插入的ID是1,影响的行数也是1。

  • ExpectCommit()用来模拟事务提交的行为。

接下来,我们调用了我们要测试的recordStats函数,并传入了模拟的数据库连接db、参数2和3。如果recordStats函数执行过程中出现了错误,我们会通过t.Errorf输出错误信息。

mock.ExpectationsWereMet() 方法会检查在使用 SQLMock 设置的各种期望操作(例如 ExpectQuery()、ExpectExec() 等)是否在测试代码中得到了满足,即这些期望操作是否被正确执行。调用 mock.ExpectationsWereMet() 方法会验证以下几点:

  • 检查所有设置的期望操作(比如预期的查询语句、预期的执行语句等)是否都有对应的实际执行操作;
  • 检查设置的期望操作是否按照预期的顺序被执行;
  • 检查是否有多余或未被预期的操作执行。
  • 如果所有的期望操作都被正确执行,mock.ExpectationsWereMet() 方法将不会返回任何错误;否则,它会返回一个错误,提示测试代码中可能存在未执行或多余执行的操作,帮助你确保测试的准确性和完整性。

 对于sql执行错误的测试用例: 

// TestShouldRollbackStatUpdatesOnFailure sql执行失败回滚的测试用例
func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {db, mock, err := sqlmock.New()if err != nil {t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)}defer db.Close()mock.ExpectBegin()mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnError(fmt.Errorf("some error"))mock.ExpectRollback()// now we execute our methodif err = recordStats(db, 2, 3); err == nil {t.Errorf("was expecting an error, but there was none")}// we make sure that all expectations were metif err := mock.ExpectationsWereMet(); err != nil {t.Errorf("there were unfulfilled expectations: %s", err)}
}
  1. ExpectBegin() 用来模拟事务开始的行为;
  2. ExpectExec("UPDATE products") 表示执行了一条名为UPDATE products的SQL语句,模拟返回的结果为sqlmock.NewResult(1, 1),表示更新一行数据,并且最后插入的ID为1;
  3. ExpectExec("INSERT INTO product_viewers") 表示执行了一条名为INSERT INTO product_viewers的SQL语句,并且传入了参数(2, 3),模拟返回的结果为错误fmt.Errorf("some error")
  4. ExpectRollback() 用来模拟事务回滚的行为。

当调用 recordStats(db, 2, 3) 函数时,如果它确实执行了 ExpectExec("INSERT INTO product_viewers").WillReturnError(fmt.Errorf("some error")) 设置的行为,则函数会返回 fmt.Errorf("some error") 错误。当函数返回错误时,测试代码会检查是否有未满足的期望,以确保测试代码和预期行为之间的一致性。如果任何一条设置的期望未被满足,mock.ExpectationsWereMet() 会返回错误,然后你就可以确定 recordStats 函数的行为与你的预期不一致。这就是为什么你在测试用例的最后会看到一行代码 if err := mock.ExpectationsWereMet(); err != nil { ... } 的原因。


使用 miniredis 进行 Redis 相关操作的单元测试

miniredis 作为单元测试用的纯Go实现的 Redis 服务器,用于模拟真实的 Redis 服务。当我们为一些包含Redis操作的代码编写单元测试时就可以使用它来mock Redis操作。

以下这个要测试 DoSomethingWithRedis 函数主要有以下几个功能:

  1. 它检查传入的 key 是否在 Redis 中 KeyValidWebsite 集合中。如果不存在,则返回 false
  2. 如果 key 存在于 KeyValidWebsite 集合中,函数会检查对应 key 的值,如果值的前缀不是 https://,则给值加上这个前缀。
  3. 然后,函数使用 rdb.Set 将一个名为 "blog" 的键设置为新的值,并设置这个键的过期时间为 5 秒。如果出现错误,会返回 false,否则返回 true
const (KeyValidWebsite = "app:valid:website:list"
)func DoSomethingWithRedis(rdb *redis.Client, key string) bool {// 这里可以是对redis操作的一些逻辑ctx := context.TODO()if !rdb.SIsMember(ctx, KeyValidWebsite, key).Val() {return false}val, err := rdb.Get(ctx, key).Result()if err != nil {return false}if !strings.HasPrefix(val, "https://") {val = "https://" + val}// 设置 blog key 五秒过期if err := rdb.Set(ctx, "blog", val, 5*time.Second).Err(); err != nil {return false}return true
}

使用miniredis库为DoSomethingWithRedis函数编写的单元测试代码。在单元测试中,我们需要模拟Redis服务器,所以会调用miniredis.Run函数创建一个模拟的Redis服务器,然后使用miniredis提供的s.Set方法设置数据。

func TestDoSomethingWithRedis(t *testing.T) {// mock一个redis servers, err := miniredis.Run()if err != nil {panic(err)}defer s.Close()// 准备数据s.Set("fengmi", "fengmi.com")s.SAdd(KeyValidWebsite, "fengmi")// 连接mock的redis serverrdb := redis.NewClient(&redis.Options{Addr: s.Addr(), // mock redis server的地址})// 调用函数ok := DoSomethingWithRedis(rdb, "fengmi")if !ok {t.Fatal()}// 可以手动检查redis中的值是否复合预期if got, err := s.Get("blog"); err != nil || got != "https://fengmi.com" {t.Fatalf("'blog' has the wrong value")}// 也可以使用帮助工具检查s.CheckGet(t, "blog", "https://fengmi.com")// 过期检查s.FastForward(5 * time.Second) // 快进5秒if s.Exists("blog") {t.Fatal("'blog' should not have existed anymore")}
}

场景三:mock接口测试

除了网络和数据库等外部依赖之外,我们在开发中也会经常用到各种各样的接口类型。

gomock是Go官方提供的测试框架,它在内置的testing包或其他环境中都能够很方便的使用。我们使用它对代码中的那些接口类型进行mock,方便编写单元测试。

mockgen 的主要用途是在单元测试中模拟(Mock)或替换(Stub)对外部依赖的对象或接口,从而保证测试的可靠性和独立性。

假设你正在编写一个应用程序,其中有一个 UserService 接口,定义了一系列与用户相关的操作:

type UserService interface {GetUserByID(userID string) (*User, error)CreateUser(user *User) errorUpdateUser(user *User) errorDeleteUser(userID string) error
}

现在,你要测试一个 UserHandler 类,它依赖 UserService 接口,并且你不希望测试过程中真正操作数据库或者执行网络请求等,因为这些操作可能会引起诸多问题(数据库连接、网络不稳定、操作权限等)在这个场景下,你希望在单元测试中使用 Mock 对象来模拟 UserService 接口的行为。使用 mockgen 工具,你可以很容易地生成一个 UserService 的 Mock 对象。 

首先,安装 mockgen 工具:

go install github.com/golang/mock/mockgen@v1.6.0

然后,你可以使用以下命令生成 UserService 的 Mock 对象:

mockgen -destination=mocks/user_mock.go -package=mocks . UserService
  1. mockgen:是 github.com/golang/mock/mockgen 的可执行文件,用于生成 mock 对象。

  2. -destination=mocks/user_mock.go:表示生成的 mock 对象将保存在 mocks/user_mock.go 文件中。 -destinationmockgen 的选项,用于指定生成文件的目标路径。这里我们选择了在 mocks 文件夹下创建 user_mock.go 文件。

  3. -package=mocks:表示生成的 mock 对象将被放置在 mocks 包下。 -packagemockgen 的选项,用于指定生成文件的包名。

  4. .:表示当前路径下的包。在这个例子中,我们希望生成 UserService 接口的 Mock 对象,而 UserService 接口是在当前路径下的包中定义的。

  5. UserService:表示要生成 Mock 对象的接口名。在这个例子中,我们希望生成的是 UserService 接口的 Mock 对象。

综上所述,这个命令的含义就是:使用 mockgen 工具,生成 UserService 接口的 Mock 对象,并保存到 mocks/user_mock.go 文件中,放置在 mocks 包下。

 生成的 mocks/user_mock.go 文件内容如下:

package mocksimport ("github.com/golang/mock/gomock"
)type MockUserService struct {ctrl     *gomock.Controllerrecorder *MockUserServiceMockRecorder
}type MockUserServiceMockRecorder struct {mock *MockUserService
}func NewMockUserService(ctrl *gomock.Controller) *MockUserService {mock := &MockUserService{ctrl: ctrl}mock.recorder = &MockUserServiceMockRecorder{mock}return mock
}func (_m *MockUserService) EXPECT() *MockUserServiceMockRecorder {return _m.recorder
}

使用 Mock 对象的示例代码如下: 

func TestUserHandler_CreateUser(t *testing.T) {ctrl := gomock.NewController(t)// gomock.NewController(t) 创建了一个 *gomock.Controller 对象,// 这个对象负责管理 mock 对象。// 通过传递 *testing.T 类型的参数 t,确保在测试过程中的任何断言失败时,都会将测试标记为失败状态。defer ctrl.Finish()// defer 关键字用于推迟执行一个函数调用,保证这个函数调用会在测试函数结束后被调用。// 这里的 ctrl.Finish() 用于释放 mock 对象。它的执行时机是在函数返回之前,不论函数是正常返回还是由于 panic 返回。mockUserService := mocks.NewMockUserService(ctrl)// 创建 mockUserService 对象,这是 UserService 接口的 mock 对象。// 使用 mocks.NewMockUserService(ctrl),传入 ctrl *gomock.Controller 对象,表示这个对象将管理 mockUserService。mockUserService.EXPECT().CreateUser(gomock.Any()).Return(nil)// 设置 mockUserService 的行为,告诉 mock 对象在被调用的时候应该执行什么操作。// 这行代码表示 mockUserService 的 CreateUser 方法应该被调用,// 参数可以是任意值,即 gomock.Any(),// 并且返回值应该是 nil,即 Return(nil)。userHandler := UserHandler{UserService: mockUserService}// 实例化被测试的 UserHandler 类,并将 mockUserService 对象传递给 UserService 属性。// Your test code here// 这里是测试代码,你可以在这里写测试逻辑,比如调用 userHandler 的方法并对结果进行断言。// 不过这里的示例代码中没有测试逻辑。
}

对于生成的 Mock 对象,它包含了接口的所有方法,并且这些方法的执行逻辑都是默认的空实现。这就意味着,你可以在测试代码中调用 Mock 对象的方法,但是它们的执行逻辑是不会真正做任何实际的操作的。这些方法都是以 EXPECT() 开头的,表示你可以通过调用 EXPECT() 方法来设置该方法的期望行为。比如,我们在单元测试代码中调用EXPECT().CreateUser(gomock.Any()).Return(nil) 表示我们期望 CreateUser 方法会被调用,而且返回值是 nil

软件测试中的打桩是指用一些代码(桩stub)代替目标代码,通常用来屏蔽或补齐业务逻辑中的关键代码方便进行单元测试。上面代码中就用到了打桩,当调用.CreateUser函数时就返回nil的返回值。


 场景四:使用monkey为任意函数及方法进行打桩

Monkey库是一个Go单元测试中常用的打桩工具,它在运行时通过汇编语言重写可执行文件,将目标函数或方法的实现跳转到桩实现,其原理类似于热补丁。

安装方式 : 

go get bou.ke/monkey

monkey库很强大,但是使用时需注意以下事项:

  • 内联函数是一种编译优化技术,它允许编译器将函数调用直接替换为函数体中的代码,从而减少函数调用的开销,提高程序的执行效率。Go语言中有一些函数是由编译器自动内联的,这样的函数称为内联函数。然而,Monkey库在运行时通过汇编语言重写可执行文件,将目标函数或方法的实现跳转到桩实现,所以它不支持内联函数。因此,在使用Monkey库进行单元测试时,需要通过命令行参数 -gcflags=-l 关闭Go语言的内联优化,这样可以保证 Monkey 库能够正确地覆盖目标函数或方法的实现。
  • Monkey库不是线程安全的,因此不要把它用到并发的单元测试中。在并发测试中,如果多个线程同时调用了Monkey库提供的API来替换同一个目标函数或方法的实现,就有可能导致竞态条件或数据竞争等问题,从而产生不可预料的错误。因此,为了保证测试的可靠性,我们需要确保在并发测试中不要使用Monkey库。

 

假设我们有一个非常简单的工具包,其中有一个名为 Add 的函数,其实现如下:

package utilsfunc Add(a, b int) int {return a + b
}

 我们在测试代码过程中想要调用这个函数,但是如果这个函数目前还没有实现,我们使用monkey库对utils.Add进行打桩。

func TestAdd(t *testing.T) {// 创建一个monkey.Patch,用于替换Add函数patch := monkey.Patch(Add, func(a, b int) int {return a * b // 使用乘法代替加法})defer patch.Unpatch()// 调用Add函数,并检查返回值result := Add(2, 3)if result != 6 { // 因为现在Add函数的实现被打桩了,返回值应该是乘法的结果 6t.Errorf("Unexpected result: %d", result)}

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

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

纽约纳斯达克大屏投放受众群体有哪些-大舍传媒

纽约纳斯达克大屏投放受众群体有哪些-大舍传媒 1. 纳斯达克大屏的概述 纳斯达克大屏是全球金融市场中最出名的电子交易平台之一。作为一个重要的金融信息传递渠道,纳斯达克大屏吸引了来自全球的投资者的目光。在这个巨大的投放平台上,大舍传媒希望为客…

1.QT简介(介绍、安装,项目创建等)

1. QT介绍 Qt(官方发音 [kju:t])是一个跨平台的C开发库,主要用来开发图形用户界面(Graphical User Interface,GUI)程序 Qt 是纯 C 开发的,正常情况下需要先学习C语言、然后在学习C然后才能使用…

计算机设计大赛 深度学习实现行人重识别 - python opencv yolo Reid

文章目录 0 前言1 课题背景2 效果展示3 行人检测4 行人重识别5 其他工具6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于深度学习的行人重识别算法研究与实现 ** 该项目较为新颖,适合作为竞赛课题方向&#xff0c…

【前端素材】推荐优质后台管理系统cassie平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面,通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面,使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…

【JavaEE】_前端使用GET请求的queryString向后端传参

目录 1. GET请求的query string 2. 关于query string的urlencode 1. GET请求的query string 1. 在HttpServletRequest请求中,getParameter方法用于在服务器这边获取到请求中的参数,主要在query string中; query string中的键值对都是程序…

Docker容器(3)单容器管理

一、单容器 1.1概念简介 Docker三个重要概念: 仓库(Repository); 镜像(Image); 容器(Container). *Docker的三个重要概念是仓库(Repository)、镜像(Image)和容器(Container)**。具体如下: **镜像(Image)**:Docker镜像是创建容器的基础,它类似…

SpringCloud(17)之SpringCloud Stream

一、Spring Cloud Stream介绍 Spring Cloud Stream是一个框架,用于构建与共享消息系统连接的高度可扩展的事件驱动微服务。该框架提供了一个灵活的编程模型,该模型建立在已经建立和熟悉的Spring习惯用法和最佳实践之上,包括对持久发布/子语义…

《读写算》杂志社读写算杂志社2024年第2期目录

教育资讯 教育部印发通知部署:做好2024年寒假期间校外培训治理工作 1《读写算》投稿:cn7kantougao163.com 北京提升学校心理健康工作水平——每校至少配备一名专职心理健康教育教师 1 湖北孝感:2026年达成小学毕业时人人会游泳 2…

C语言编程安全规范

目的 本规范旨在加强编程人员在编程过程中的安全意识,建立编程人员的攻击者思维,养成安全编码的习惯,编写出安全可靠的代码。 2 宏 2.1 用宏定义表达式时,要使用完备的括号 2.2 使用宏时,不允许参数发生变化 3 变量 …

关于页表,页号,物理块号的例题

课本上的图解 题目:在分页系统中地址结构长度为16位,页面大小为2K,作业地址空间为6K,该作业的各页依次存放在2、3、6号物理块中,相对地址2500处有一条指令store 1, 4500,请给出该作业的页表,该指令的物理单…

css5定位

css 一.定位1.概念(定位定位模式边位移)2.静态位移static(不常用)3.相对定位relative(不脱标)(占位置)4.绝对定位absolute(脱标)(不占位置&#x…

Thomson(汤姆森)简化了其螺旋千斤顶产品的CAD选型配置

线性运动控制解决方案提供商Thomson在其在线工程设计工具中添加了独特的螺旋千斤顶配置和选择工具。新的Thomson螺旋千斤顶产品选型器可帮助设计工程师优化和选定螺旋千斤顶,以满足高达100吨的负载应用。 Thomson螺旋千斤顶产品系列负责人Mitch Katona说&#xff1…

Java学习--学生管理系统(残破版)

代码 Main.java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {ArrayList<Student> list new ArrayList<>();loop:while (true) {System.out.println("-----欢迎来到阿宝院校学生管理系…

docker (十二)-私有仓库

docker registry 我们可以使用docker push将自己的image推送到docker hub中进行共享&#xff0c;但是在实际工作中&#xff0c;很多公司的代码不能上传到公开的仓库中&#xff0c;因此我们可以创建自己的镜像仓库。 docker 官网提供了一个docker registry的私有仓库项目&#…

MySQL的21个SQL经验

1. 写完SQL先explain查看执行计划(SQL性能优化) 日常开发写SQL的时候,尽量养成这个好习惯呀:写完SQL后,用explain分析一下,尤其注意走不走索引。 explain select userid,name,age from user where userid =10086 or age =18;2、操作delete或者update语句,加个limit(S…

C#之WPF学习之路(5)

目录 内容控件&#xff08;2&#xff09; TextBlock文字块 TextBox文本框 TextBoxBase基类 TextBox控件 RichTextBox富文本框 ToolTip控件&#xff08;提示工具&#xff09; Popup弹出窗口 Image图像控件 属性成员 事件成员 内容控件&#xff08;2&#xff09; Tex…

Maven【4】(继承)(命令行操作)

文章目录 【1】基础概念【2】继承的作用【3】创建父工程和子工程【4】在父工程中统一管理依赖 【1】基础概念 说到继承&#xff0c;我们很容易想到Java中的继承&#xff0c;有子类和父类&#xff0c;子类继承父类&#xff0c;那么我们maven中的继承是什么呢&#xff1f; Maven…

海外媒体推广发稿平台之选快速提升品牌知名度的8个方法-华媒舍

随着全球化趋势的加深&#xff0c;海外市场对于企业来说变得越来越重要。在海外媒体上宣传品牌是提升知名度和开拓新客户的关键步骤。本文将为您介绍8个使用海外媒体推广发稿平台来快速提升品牌知名度的秘籍。 1. 选择可信赖的平台 在选择推广发稿平台时&#xff0c;首先要确保…

化妆品汞含量检测FDA 21 CFR700.13 标准

化妆品汞含量检测FDA 21 CFR700.13 标准 化妆品汞含量检测FDA 21 CFR700.13 标准 汞是一种有毒的元素&#xff0c;在化妆品中是一种禁用的成分。然而&#xff0c;汞在一些非正规的产品中仍然存在&#xff0c;给消费者的健康带来了潜在的危害。因此&#xff0c;对于化妆品生产企…

正向代理的反爬虫与防DDoS攻击:保护网站免受恶意行为

目录 前言 一、正向代理的原理 二、正向代理的反爬虫功能 1. IP地址隐藏 2. 请求多样化 三、正向代理的防DDoS攻击功能 1. 均衡负载 2. IP过滤 结论 前言 在当前互联网环境下&#xff0c;网站常常受到各种恶意行为的侵袭&#xff0c;其中包括爬虫和DDoS攻击。这些行为…
推荐文章