{"id":20464521,"url":"https://github.com/bytedance/nxt_unit","last_synced_at":"2025-04-13T08:37:38.241Z","repository":{"id":98569518,"uuid":"608090831","full_name":"bytedance/nxt_unit","owner":"bytedance","description":"NxtUnit is an automatically unit test generation application for Go.","archived":false,"fork":false,"pushed_at":"2024-03-07T10:31:42.000Z","size":681,"stargazers_count":75,"open_issues_count":6,"forks_count":13,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-27T00:13:18.229Z","etag":null,"topics":["codegenerator","go","golang","testing","unittest"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bytedance.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-01T09:53:17.000Z","updated_at":"2025-03-16T13:34:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"f047e505-64e3-48e4-a8b1-8d157a8ebd2c","html_url":"https://github.com/bytedance/nxt_unit","commit_stats":{"total_commits":39,"total_committers":4,"mean_commits":9.75,"dds":0.1282051282051282,"last_synced_commit":"c9699efdc81cb49bf8a202e657752f1dfdc7e1fe"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytedance%2Fnxt_unit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytedance%2Fnxt_unit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytedance%2Fnxt_unit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bytedance%2Fnxt_unit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bytedance","download_url":"https://codeload.github.com/bytedance/nxt_unit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248685126,"owners_count":21145210,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codegenerator","go","golang","testing","unittest"],"created_at":"2024-11-15T13:15:31.562Z","updated_at":"2025-04-13T08:37:38.193Z","avatar_url":"https://github.com/bytedance.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NxtUnit\n`NxtUnit` is an automatically unit test generation application for Go.\\\nYou can compile it as the binary package and run it.\n\n[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue)](https://github.com/bytedance/nxt_unit/blob/master/LICENSE)\n[![Go](https://github.com/bytedance/nxt_unit/actions/workflows/go.yml/badge.svg)](https://github.com/bytedance/nxt_unit/actions/workflows/go.yml)\n[![codecov](https://codecov.io/gh/bytedance/nxt_unit/branch/main/graph/badge.svg)](https://codecov.io/gh/bytedance/nxt_unit)\n## Table of Contents\n\n- [Introduction](#Introduction)\n- [How To Use](#How-To-Use)\n- [Generation Failure](#Generation-Failure)\n- [Documentations](#Documentations)\n\n## Introduction\n\nAutomated unit test generation has been studied for a long time and prior research has focused on dynamically compiled or \ndynamically typed programming languages such as Java and Python. However, few of the existing tools support Go, \nwhich is a popular statically compiled and typed programming language in the industry for server application development \nand used extensively in our production environment\n\n`NxtUnit` is the tool that can automatically generate the unit test for Go. For example, given the original code\n```Go\nfunc Example (input1 int, input2 int) {\n   if input1*input2 \u003e 9 {\n      return input1\n   }\n   switch input1 {\n   case 20:\n      input1 = +RPCCallee1(input2)\n   case 40:\n      input1 = +RPCCallee1(input2)\n   }\n   return input1\n}\n```\n\nDuring the generation, you might see our intermediate code:\n\n```Go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tatgconstant \"github.com/bytedance/nxt_unit/atgconstant\"\n\tcontexthelper \"github.com/bytedance/nxt_unit/atghelper/contexthelper\"\n\tmockfunc \"github.com/bytedance/nxt_unit/codebuilder/mock\"\n\tvariablecard \"github.com/bytedance/nxt_unit/codebuilder/variablecard\"\n\tduplicatepackagemanager \"github.com/bytedance/nxt_unit/manager/duplicatepackagemanager\"\n\tstaticcase \"github.com/bytedance/nxt_unit/staticcase\"\n\tconvey \"github.com/smartystreets/goconvey/convey\"\n\timports \"golang.org/x/tools/imports\"\n)\n\nfunc TestDAUAMW(t *testing.T) {\n\twgAUAMW := sync.WaitGroup{}\n\tt.Parallel()\n\toriginPath := \"/Users/siweiwang/go/src/github.com/nxt_unit/siwei.go\"\n\tdeclLocker := sync.RWMutex{}\n\tdeclData := map[string][]string{}\n\tuseMockMap := map[string]map[string]int{}\n\ttype DeclResult struct {\n\t\tAvailableList []bool\n\t\tPathSync      sync.Map\n\t}\n\tdeclStatistics := map[string]DeclResult{}\n\tsmartUnitCtx := duplicatepackagemanager.SetInstance(context.Background())\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"errors\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"context\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"fmt\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"strings\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"sync\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"testing\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"time\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"syscall\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"runtime/debug\")\n\tduplicatepackagemanager.GetInstance(smartUnitCtx).PutAndGet(\"\", \"runtime/stack\")\n\n\twgAUAMW.Add(1)\n\tgo func(t *testing.T) {\n\t\ttype Args struct {\n\t\t\tInput1 int\n\t\t\tInput2 int\n\t\t}\n\t\ttype test struct {\n\t\t\tName  string\n\t\t\tArgs  Args\n\t\t\tWant  int\n\t\t\tMocks variablecard.MocksRecord\n\t\t}\n\t\tdefer func() {\n\t\t\twgAUAMW.Done()\n\t\t}()\n\t\ttt := test{}\n\t\tduplicatepackagemanager.GetInstance(smartUnitCtx).SetRelativePath(tt)\n\t\tvar rowData []string\n\t\tuseMock := make(map[string]int, 0)\n\t\tfor i := 0; i \u003c 4; i++ {\n\t\t\tconvey.Convey(tt.Name, t, func() {\n\t\t\t\tmockRender := \u0026mockfunc.StatementRender{\n\t\t\t\t\tMockStatement:   []string{},\n\t\t\t\t\tMonkeyOutputMap: make(variablecard.MonkeyOutputMap, 0),\n\t\t\t\t\tUsedMockFunc:    make(map[string]int, 0),\n\t\t\t\t}\n\t\t\t\tsmartUnitCtx = contexthelper.SetVariableContext(smartUnitCtx, atgconstant.VariableContext{})\n\t\t\t\ttt = variablecard.VariableMutate(smartUnitCtx, reflect.TypeOf(tt), reflect.ValueOf(tt)).Interface().(test)\n\t\t\t\tdefer func() {\n\t\t\t\t}()\n\t\t\t\tif got := ExampleAUAMW(tt.Args.Input1, tt.Args.Input2); got != tt.Want {\n\t\t\t\t\ttt.Want = got\n\t\t\t\t}\n\t\t\t\ttt.Mocks = mockRender.MockStatement\n\t\t\t\tuseMock = mockRender.UsedMockFunc\n\t\t\t\trowData = append(rowData, variablecard.ValueToString(smartUnitCtx, reflect.ValueOf(tt)))\n\t\t\t})\n\t\t}\n\t\tif len(rowData) \u003c= 0 {\n\t\t\treturn\n\t\t}\n\t\tdeclLocker.Lock()\n\t\tdeclData[\"Example\"] = rowData\n\t\tuseMockMap[\"Example\"] = useMock\n\t\tdeclLocker.Unlock()\n\t}(t)\n\n\t// summary data info wait for the result of function\n\twgAUAMW.Wait()\n\tto := time.After(time.Millisecond * 3000)\nLoop:\n\tfor {\n\t\tselect {\n\t\tcase info := \u003c-WorkPipeAUAMW:\n\t\t\t// get declData KeyName\n\t\t\tdeclFuncName := info.FunctionName\n\t\t\tif info.ReceiverName != \"\" {\n\t\t\t\tdeclFuncName = info.ReceiverName + declFuncName\n\t\t\t}\n\t\t\tif info.IsStart != \"\" {\n\t\t\t\tdeclFuncName = \"*\" + declFuncName\n\t\t\t}\n\t\t\tif _, dataOk := declData[declFuncName]; dataOk {\n\t\t\t\tsResult, ok := declStatistics[declFuncName]\n\t\t\t\tif ok {\n\t\t\t\t\tsResult.AvailableList = append(sResult.AvailableList, false)\n\t\t\t\t} else {\n\t\t\t\t\tsResult.AvailableList = []bool{false}\n\t\t\t\t\tsResult.PathSync = sync.Map{}\n\t\t\t\t}\n\t\t\t\tdeclStatistics[declFuncName] = sResult\n\t\t\t}\n\n\t\t\tif info.Coverage \u003e= 0 {\n\t\t\t\t// trim panic coverage == -1\n\t\t\t\tif sResult, ok := declStatistics[declFuncName]; ok {\n\t\t\t\t\tfmt.Println(\"pathid \" + info.PathID)\n\t\t\t\t\tif _, exist := sResult.PathSync.Load(info.PathID); !exist {\n\t\t\t\t\t\t// record unique path\n\t\t\t\t\t\tsResult.PathSync.Store(info.PathID, struct{}{})\n\t\t\t\t\t\t// update available case\n\t\t\t\t\t\tsResult.AvailableList[len(sResult.AvailableList)-1] = true\n\t\t\t\t\t\tdeclStatistics[declFuncName] = sResult\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase \u003c-to:\n\t\t\tbreak Loop\n\t\t}\n\t}\n\tvar hitLine int\n\tfor _, hit := range HitSetAUAMW {\n\t\tif hit \u003e 0 {\n\t\t\thitLine++\n\t\t}\n\t}\n\tfmt.Println(fmt.Sprintf(\"coverage(%v;%v)-r \\n\", len(HitSetAUAMW), hitLine))\n\tres := map[string]string{}\n\tfor k, v := range declData {\n\t\tsResult, ok := declStatistics[k]\n\t\tif ok {\n\t\t\tdataList := make([]string, 0)\n\t\t\tfor index, available := range sResult.AvailableList {\n\t\t\t\tif available {\n\t\t\t\t\tdataList = append(dataList, v[index])\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(dataList) \u003e 0 {\n\t\t\t\tres[k] = fmt.Sprintf(\"[]test{%s}\", strings.Join(dataList, \",\"))\n\t\t\t}\n\t\t}\n\t}\n\tcode, err := staticcase.RecordFinalSuite(smartUnitCtx, originPath, res, useMockMap, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tcode, err = imports.Process(\"\", code, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttestName := strings.ReplaceAll(filepath.Base(originPath), \".go\", \"_ATG_test.txt\")\n\ttestFile := path.Join(filepath.Dir(originPath), testName)\n\tif err := ioutil.WriteFile(testFile, code, atgconstant.NewFilePerm); err != nil {\n\t\tt.Fatalf(\"[render testsuite] has ioutil.WriteFile err: %v\", err)\n\t}\n}\n```\n\nit can generate the unit test like below\n\n```Go\nimport (\n   testing \"testing\"\n   gomonkey \"github.com/agiledragon/gomonkey/v2\"\n   convey \"github.com/smartystreets/goconvey/convey\" \n)\nfunc TestExampleFunction_URRDGU(t *testing.T) {\n   type Args struct {\n      Input1 int,  Input2 int\n   }\n   type test struct {\n      Name            string\n      Args            Args\n      Want            int\n      Mocks           func()\n      MonkeyOutputMap map[string][]interface{}\n   }\n   tests := []test{test{\n      Name: \"Alice King\",\n      Args: Args{\n         Input1: 20, Input2: 4,\n      },\n      Want:            20,\n      Mocks:           func() {},\n      MonkeyOutputMap: map[string][]interface{}{\"RPCCallee1\": []interface{}{10}},\n   }, test{\n      Name: \"Eason King\",\n      Args: Args{\n         Input1: 7, Input2: 1,\n      },\n      Want:            7,\n      Mocks:           func() {},\n      MonkeyOutputMap: map[string][]interface{}{\"RPCCallee1\": []interface{}{11}},\n   }}\n   for _, tt := range tests {\n      convey.Convey(tt.Name, t, func() {\n         tt.Mocks()\n         PTNFTPatch := gomonkey.ApplyFuncReturn(RPCCallee1, tt.MonkeyOutputMap[\"RPCCallee1\"][0])\n         defer PTNFTPatch.Reset()\n    if got := ExampleFunction(tt.Args.Input1, tt.Args.Input2); got != tt.Want {\n            convey.So(got, convey.ShouldResemble, tt.Want)\n         }\n      })\n   }\n}\n```\n\n\n## How To Use\n### Installation\n```\ngo install github.com/bytedance/nxt_unit@latest\n```\n\n### Usage\n```\n-function_name(required)\n    function name\n-receiver_name(optional)\n    the receiver name of your function\n-receiver_is_star(optional) \n    whether your receiver is a pointer or not\n-usage(required)\n    option1: generate the unit test\n    option2: generate the template\n-go(optional) \n    your local go path\n-file_name(required)\n    absolute go path\n```\n###  Example\n```\ngo build\n./nxt_unit -file_path=[your path] -receiver_name=Decoder -receiver_is_star=true -function_name=Decode -usage=plugin\n-go=/usr/local/go/bin/go\n```\n### Run generated unit test\n```\ngo test xxxx_test.go -gcflags \"all=-N -l\"\n```\n`-gcflags \"all=-N -l\"` used for unblocking the inlining of the function\n## Failure Scenarios\nThe failure might be caused by the following reasons:\n1. The function is not exported\n2. The fault of the gomonkey\n3. You don't have permission to execute the file. Please see the [Solution](#Solution)\n\n### Solution for the gomonkey\n1 download the tool\n```\ncd `go env GOPATH`\ngit clone https://github.com/eisenxp/macos-golink-wrapper.git\n```\n2 rename the link to original_link\n```\nmv `go env GOTOOLDIR`/link `go env GOTOOLDIR`/original_link \n```\n3 copy tool to GOTOOLDIR\n```\ncp `go env GOPATH`/macos-golink-wrapper/link  `go env GOTOOLDIR`/link \n```\n4 authorize link\n```\nchmod +x `go env GOTOOLDIR`/link\n```\n\n## Doumentations\n### License\n\n`NxtUnit` is licensed under the terms of the Apache license 2.0. See [LICENSE](LICENSE) for more information.\n\n### Citation\n```\n@inproceedings{10.1145/3593434.3593443,\n    author = {Wang, Siwei and Mao, Xue and Cao, Ziguang and Gao, Yujun and Shen, Qucheng and Peng, Chao},\n    title = {NxtUnit: Automated Unit Test Generation for Go},\n    year = {2023},\n    isbn = {9798400700446},\n    publisher = {Association for Computing Machinery},\n    address = {New York, NY, USA},\n    url = {https://doi.org/10.1145/3593434.3593443},\n    doi = {10.1145/3593434.3593443},\n    booktitle = {Proceedings of the 27th International Conference on Evaluation and Assessment in Software Engineering},\n    pages = {176–179},\n    numpages = {4},\n    keywords = {Go, Automated Test Generation},\n    location = {Oulu, Finland},\n    series = {EASE '23}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytedance%2Fnxt_unit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbytedance%2Fnxt_unit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbytedance%2Fnxt_unit/lists"}