GO单元测试的使用

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 文件中。 -destination 是 mockgen 的选项,用于指定生成文件的目标路径。这里我们选择了在 mocks 文件夹下创建 user_mock.go 文件。

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

  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.tpcf.cn/news/709431.html

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

相关文章

PBM学习——从基础到精通!!!

本专栏着重讲解PBM学习所得,学习笔记、心得,并附有视频素材资料,视频详细目录如下: PBM相关参数解释1 PBM相关参数解释2 PBM相关案例实践1 PBM相关案例实践2 PBM相关案例实践2 PBM相关案例实践3 PBM多相流中次相界面设置1 PBM多相流中次相界面设置2 欧拉多相流曳力1 欧拉多…

make/makefile

首先什么是make和makefile呢?先简单说,make是一条命令,makefile是一个文件,这个文件中存的是依赖关系和依赖方法,那么为什么会有这两个东西呢? 首先我们知道在VS2019中,我们可以直接创建一个工程…

474 一零和

题目 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。示例 1:输入:strs ["1…

Linux系统中make/Makefile的介绍

文章目录 前言一、make命令二、makefile功能介绍1.makefile文件的编写格式2.hello.c文件内容3.makefile文件4.安装make命令 总结 前言 在linux系统中,我们对项目文件进行处理的时候会不方便,因此我们需要对文件的编译进行自动化处理。 下面就是在Linux系…

1、docker 基础命令

1、docker 运行镜像 docker run image tag 2、创建dockerfile&#xff08;构建容器的相关命令&#xff09; vim DockerFile 3、docker 构建容器镜像 docker build -t <image_name> . 4、docker 分层 5、查看镜像 docker images 6、docker 执行 docker run --name &…

web组态(BY组态)接入流程

技术文档 官网网站&#xff1a;www.hcy-soft.com 体验地址&#xff1a; www.byzt.net:60/sm 一、数据流向图及嵌入原理 数据流向 嵌入原理 二、编辑器调用业务流程图 三、集成前需要了解的 1、后台Websocket端往前台监控画面端传输数据规则 后台websocket向客户端监控画面…

模型优化_XGBOOST学习曲线及改进,泛化误差

代码 from xgboost import XGBRegressor as XGBR from sklearn.ensemble import RandomForestRegressor as RFR from sklearn.linear_model import LinearRegression as LR from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split,c…

Linux: GDB 调试工具

概念&#xff1a; Linux GDB&#xff08;GNU Debugger&#xff09;是一个功能强大的调试工具&#xff0c;用于调试C、C等编程语言的程序。它可以帮助开发人员定位和修复程序中的错误。 GDB 的使用 &#xff1a; 激活和进入工作模式&#xff1a; gdb 需要调试的文件 进入 …

【Java设计模式】三、

文章目录 0、案例&#xff1a;咖啡屋1、简单工厂模式 静态工厂&#xff08;不属于23种之列&#xff09;2、工厂方法模式3、抽象工厂模式4、建造者模式5、原型设计模式 0、案例&#xff1a;咖啡屋 模拟咖啡店点餐。咖啡有多种&#xff0c;抽象类&#xff0c;子类为各种咖啡。咖…

MySQL查询数据不一致

在您提供的两个查询中&#xff0c;您发现了查询结果的数据量不一致&#xff0c;一个查询结果为404条&#xff0c;另一个查询结果为410条。为了优化这两个查询并确保结果的一致性&#xff0c;您可以尝试以下几点&#xff1a; 1. **统一字段命名**&#xff1a;在两个查询中确保使…

前端面试题汇总(一)

1.html语义化 HTML语义化是指在编写HTML代码时&#xff0c;尽可能使用具有明确含义的标签来描述页面内容的结构和意义&#xff0c;以便让浏览器、搜索引擎和开发者更好地理解和解释页面内容。通过使用语义化的HTML标签&#xff0c;可以提高页面的可访问性、可维护性和可读性&a…

2.29作业

T课上实现通信代码总结&#xff1a; 程序代码&#xff1a; TCPSER.c #include<myhead.h> #define SER_IP "192.168.244.140" //服务器IP #define SER_PORT 9999 //服务器端口号 int main(int argc, const char *argv[]) {//1.创建用于监…

为什么猫咪挑食不吃猫粮?适口性好、普口性价的主食冻干推荐

现代养猫人士往往把自家的小猫看作是生活中的小宝贝&#xff0c;十分宠爱。最令人头疼的就是猫咪挑食不吃猫粮&#xff0c;为什么猫咪挑食不吃猫粮&#xff1f;猫咪挑食应该怎么办&#xff1f;今天为大家分享一个既不让咱宝贝猫咪受罪又可以改善猫咪挑食的方法。 一、为什么猫咪…

深入理解nginx的https sni机制

目录 1. 概述2. 初识sni3. nginx的ssl证书配置指令3.1 ssl_certificate3.2 ssl_certificate_key3.3 ssl_password_file4. nginx源码分析4.1 给ssl上下文的初始化4.2 连接初始化4.3 处理sni回调4.2 动态证书的加载5. 总结阅读姊妹篇: 深入理解nginx的https alpn机制 1. 概述 SN…

Vue+SpringBoot打造音乐偏好度推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 音乐档案模块2.1.2 我的喜好模块2.1.3 每日推荐模块2.1.4 通知公告模块 2.2 用例图设计2.3 实体类设计2.4 数据库设计 三、系统展示3.1 登录注册3.2 音乐档案模块3.3 音乐每日推荐模块3.4 通知公告模…

javase_进阶 day8 递归,异常

递归 递归介绍 方法直接或者间接调用本身注意&#xff1a;递归如果没有控制好终止&#xff0c;会出现递归死循环&#xff0c;导致栈内存溢出现象一些算法题的实现, 都需要使用递归 public class RecursionDemo1 {/*递归介绍: 方法直接或者间接调用本身*/public static void …

外包干了6个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了重庆一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

Dockerfile(1) - FROM 指令详解

FROM 指明当前的镜像基于哪个镜像构建dockerfile 必须以 FROM 开头&#xff0c;除了 ARG 命令可以在 FROM 前面 FROM [--platform<platform>] <image> [AS <name>]FROM [--platform<platform>] <image>[:<tag>] [AS <name>]FROM […

搭建独立节点通常涉及哪些步骤

1.了解独立节点搭建的基础概念和原理。这包括理解独立节点搭建的含义&#xff0c;即通过云服务器等方式单独搭建自己的网站&#xff0c;以 确保网站的专属性和自主性&#xff0c;并更好地控制数据流动和安全性。1 2.选择服务器类型和配置。这包括根据需求选择合适的服务器&a…

shardingsphere 集成springboot【水平分表】

创建sharding_sphere数据库 在数据库中创建两张表&#xff0c;t_order_1和t_order_2 分片规则&#xff1a;如果订单编号是偶数添加到t_order_1,如果是奇数添加到t_order_2 创建实体类 public class Order { private Integer id; private Integer orderType; private Int…