{"id":20290318,"url":"https://github.com/ksss/rgot","last_synced_at":"2025-04-11T10:59:23.138Z","repository":{"id":35726733,"uuid":"40005080","full_name":"ksss/rgot","owner":"ksss","description":"Ruby + GOlang Testing = Rgot","archived":false,"fork":false,"pushed_at":"2025-03-08T11:15:16.000Z","size":145,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-25T07:23:12.815Z","etag":null,"topics":["ruby","testing"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ksss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-31T12:49:36.000Z","updated_at":"2025-03-08T11:15:20.000Z","dependencies_parsed_at":"2023-01-16T04:13:30.140Z","dependency_job_id":null,"html_url":"https://github.com/ksss/rgot","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Frgot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Frgot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Frgot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Frgot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ksss","download_url":"https://codeload.github.com/ksss/rgot/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248381707,"owners_count":21094525,"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":["ruby","testing"],"created_at":"2024-11-14T15:06:59.924Z","updated_at":"2025-04-11T10:59:23.126Z","avatar_url":"https://github.com/ksss.png","language":"Ruby","readme":"Rgot\n===\n\n[![Ruby](https://github.com/ksss/rgot/actions/workflows/main.yml/badge.svg)](https://github.com/ksss/rgot/actions/workflows/main.yml)\n\nRuby + Golang Testing = Rgot\n\nRgot is a testing package convert from golang testing.\n\n### Usage\n\ntest/sample.rb\n\n```ruby\nclass Sample\n  def sum(i, j)\n    i + j\n  end\nend\n```\n\ntest/pass_test.rb\n\n```ruby\nmodule SampleTest\n  class TypeSum \u003c Struct.new(:left, :right, :expect)\n  end\n\n  DATA = [\n    TypeSum.new(2, 3, 5),\n    TypeSum.new(12, 9, 21),\n    TypeSum.new(85, 42, 127),\n  ]\n\n  def test_pass(t)\n    s = Sample.new\n    DATA.each do |ts|\n      sum = s.sum(ts.left, ts.right)\n      unless sum.kind_of?(Integer)\n        t.error(\"expect Integer got #{sum.class}\")\n      end\n      unless sum == ts.expect\n        t.error(\"expect #{ts.expect} got #{sum}\")\n      end\n    end\n  end\nend\n```\n\n```\n$ rgot -v --require ./test/sample test/pass_test.rb\n=== RUN test_pass\n--- PASS: test_pass (0.00003s)\nPASS\nok\t0.001s\n```\n\n# Features\n\n## Testing\n\nI provide a very simple testing feature to you.\n\n**Rgot** testing is quite different from *RSpec* and *MiniTest* etc.\n\nRgot carve out a new world of testing.\n\nSo, You check only bad case in testing.\n\n## Benchmark\n\nYou can write simple benchmark script with testing.\n\nThis benchmark to adjust the time automatically.\n\n```ruby\nmodule FooTest\n  def benchmark_something(b)\n    i = 0\n    while i \u003c b.n\n      something(1)\n      i += 1\n    end\n  end\nend\n```\n\n```\n$ rgot foo_test.rb --bench .\nbenchmark_something\t14400000\t81 ns/op\nok\tFooTest\t2.782s\n```\n\n`b.n` is automatically adjusted.\n\n## Fuzzing\n\n```\n$ rgot target_file_test.rb --fuzz . --fuzztime 1\n```\n\nFuzzing tests are also supported.\nPlease refer to the gloang documentation for details.\n\nhttps://go.dev/security/fuzz/\n\n```ruby\nmodule FooTest\n  # To enable fuzzing, the method name\n  # should be prefixed with `fuzz`.\n  def fuzz_any_func(f)\n    f.add(5, \"hello\")\n    f.fuzz do |t, i, s|\n      out, err = foo(i, s)\n      if err != nil \u0026\u0026 out != \"\"\n        t.errorf(\"%s, %s\", out, err)\n      end\n    end\n  end\nend\n```\n\n## Example\n\nRgot's example feature is the best and if you want to write the sample code of your library.\n\nWhile presenting the sample code, it will be able to test whether the display results match at the same time.\n\n```ruby\nmodule FooTest\n  class User\n    def initialize(name)\n      @name = name\n    end\n\n    def hello\n      \"Hello #{@name}\"\n    end\n  end\n\n  def example_something\n    user = User.new('ksss')\n    puts user.hello\n    # Output:\n    # Hello ksss\n  end\n\n  def example_fail\n    user = User.new('ksss')\n    puts user.hello\n    # Output:\n    # Hi ksss\n  end\nend\n```\n\n`example_fail` fail since output is different.\n\nSo, you can notice that the sample code is wrong.\n\n# Table Driven Tests\n\n```rb\nFLAGTESTS = [\n  [\"%a\", \"[%a]\"],\n  [\"%-a\", \"[%-a]\"],\n  [\"%+a\", \"[%+a]\"],\n  [\"%#a\", \"[%#a]\"],\n  [\"% a\", \"[% a]\"],\n  [\"%0a\", \"[%0a]\"],\n  [\"%1.2a\", \"[%1.2a]\"],\n  [\"%-1.2a\", \"[%-1.2a]\"],\n  [\"%+1.2a\", \"[%+1.2a]\"],\n  [\"%-+1.2a\", \"[%+-1.2a]\"],\n  [\"%-+1.2abc\", \"[%+-1.2a]bc\"],\n  [\"%-1.2abc\", \"[%-1.2a]bc\"],\n]\n\ndef test_flag_parser(t)\n  FLAGTESTS.each do |input, output|\n    s = Flag.print(input)\n    unless s == output\n      t.errorf(\"Flag.print(%p) =\u003e %p, want %p\", input, s, output)\n    end\n  end\nend\n```\n\nsee https://github.com/golang/go/wiki/TableDrivenTests\n\n# Naming convention\n\n## Filename\n\nFilename should be set '*_test.rb'\n\n## Module name\n\nModule name should be set 'XxxTest'\n\n'Xxx' can replace any string (in range of ruby module)\n\nTesting code file can split any number.\n\nBut all file should be have one module (like golang package name).\n\n```ruby\nmodule XxxTest\n  # ...\nend\n```\n\n## Method name\n\nMethod name should be set `test_*` for testing.\n\nAnd benchmark method should be set `benchmark_*`.\n\nAnd fuzz method should be set `fuzz_*`.\n\nAnd example method should be set `example_*`.\n\n```ruby\nmodule XxxTest\n  def test_any_name(t)\n  end\n\n  def benchmark_any_name(b)\n  end\n\n  def fuzz_any_name(f)\n  end\n\n  def example_any_name\n  end\nend\n```\n\n# Command line interface\n\n```\n$ rgot -h\nUsage: rgot [options]\n    -v, --verbose                    log all tests\n        --version                    show Rgot version\n        --bench [regexp]             benchmark\n        --benchtime [sec]            benchmark running time\n        --timeout [sec]              set timeout sec to testing\n        --cpu [count,...]            set cpu counts of comma split\n        --thread [count,...]         set thread counts of comma split\n        --require [path]             load some code before running\n        --load-path [path]           Specify $LOAD_PATH directory\n        --fuzz [regexp]              run the fuzz test matching `regexp`\n        --fuzztime [sec]             time to spend fuzzing; default is to run indefinitely\n```\n\n## Basic\n\n```\n$ rgot file_of_test.rb\nPASS\nok\t0.001s\n```\n\nSet filename to argument.\n\nJust only start testing file_of_test.rb.\n\n```\n$ rgot sample\nPASS\nok\t0.002s\n```\n\nAnd set dirname to argument, run all case of testing under this dir.\n\n## Verbose\n\n```\n$ rgot -v target_file_test.rb\n=== RUN test_pass\n--- PASS: test_pass (0.00005s)\nPASS\nok\t0.001s\n```\n\nShow all log and more detail information of testing.\n\n## Benchmark\n\n```\n$ rgot target_file_test.rb --bench .\n```\n\nRun testing with benchmark.\n\n`.` means match all string for regexp.\n\nSet `someone` if you only run benchmark to match `someone` method.(e.g. benchmark_someone_1)\n\n### Parallel benchmark\n\nBenchmark for parallel performance.\n\n`--cpu` option set process counts (default `Etc.nprocessors`).\n\nAnd `--thread` option set thread counts (default 1).\n\nBenchmark fork, run and report each by process counts.\n\n(**process** and **thread** means ruby/linux native process and thread)\n\n```ruby\nmodule FooTest\n  def benchmark_any_func(b)\n    b.run_parallel do |pb|\n      # pb is instance of Rgot::PB\n      # call some time by b.n\n      while pb.next\n        some_func()\n      end\n    end\n  end\nend\n```\n\n```\n$ rgot foo_test.rb --bench . --cpu=2,4 --thread=2,4\nbenchmark_any_func-2(2)\t40\t13363604 ns/op\nbenchmark_any_func-2(4)\t160\t7125845 ns/op\nbenchmark_any_func-4(2)\t160\t7224815 ns/op\nbenchmark_any_func-4(4)\t320\t3652431 ns/op\nok\tFooTest\t3.061s\n```\n\n## Timeout\n\n```\n$ rgot target_file_test.rb --timeout 3\n```\n\nYou can set timeout sec for testing (default 0).\n\nFail testing and print raised exception message to STDERR if timeout.\n\n# Recommendation\n\nSet up Rakefile.\n\n```rb\n# Rakefile\nrequire \"rake/testtask\"\nRake::TestTask.new do |task|\n  task.libs = %w[lib test]\n  task.test_files = FileList[\"lib/**/*_test.rb\"]\nend\n```\n\nSet `test/test_helper.rb`.\n\n```rb\n# test/test_helper.rb\nrequire \"rgot/cli\"\n\nunless $PROGRAM_NAME.end_with?(\"/rgot\")\n  at_exit do\n    exit Rgot::Cli.new([\"-v\", \"lib\"]).run\n  end\nend\n```\n\nPlace the test file in the same directory as the implementation file.\nJust like in golang.\n\n```console\n$ ls lib\nlib/foo.rb\nlib/foo_test.rb\n```\n\nWrite your testing code.\n\n```rb\n# lib/foo_test.rb\nrequire 'test_helper'\n\nmodule FooTest\n  def test_foo(t)\n    # ...\n  end\nend\n```\n\nOK, You will be able to both run all tests with rake and specify one file to run.\n\n```console\n$ bundle exec rake test\n```\n\n```console\n$ bundle exec rgot lib/foo_test.rb\n```\n\n# Methods\n\n## Rgot\n\n### Rgot.benchmark\n\nRun benchmark function without framework.\n\n```ruby\nresult = Rgot.benchmark do |b|\n  i = 0\n  while i \u003c b.n\n    some_func()\n    i += 1\n  end\nend\nputs result #=\u003e 100000\t100 ns/op\n```\n\n### Rgot.verbose?\n\nCheck running with option verbose true/false.\n\n## Rgot::M (Main)\n\nMain method run first on testing.\n\nAnd this is default virtual main code.\n\n```ruby\nmodule TestSomeCode\n  def test_main(m)\n    m.run\n  end\nend\n```\n\nMain method should be set 'test_main' only.\n\nVariable `m` is a instance of `Rgot::M` class means Main.\n\n`Rgot::M#run` start all testing methods.\n\nAnd return code of process end status.\n\nIf you want to run before/after all testing method, You can write like this.\n\n```ruby\nmodule TestSomeCode\n  def test_main(m)\n    the_before_running_some_code\n    code = m.run\n    the_after_running_some_code\n    code\n  end\nend\n```\n\n## Rgot::Common\n\n`Rgot::Common` is inherited to `Rgot::T` and `Rgot::B`\n\n`Rgot::Common` have some logging method.\n\n### Rgot::Common#log\n\n```ruby\nt.log(\"wooooo\", 1, 2, 3)\n```\n\nWrite any log message.\n\nBut this message to show need -v option.\n\n### Rgot::Common#logf\n\nWrite any log message like sprintf.\n\n```ruby\nt.logf(\"%d-%s\", 10, \"foo\")\n```\n\n### Rgot::Common#error\n\n```ruby\nt.error(\"expect #{a} got #{b}\")\n```\n\nTest fail and show some error message.\n\n### Rgot::Common#errorf\n\nFail loggin same as logf\n\n### Rgot::Common#fatal\n\nTesting stop and fail with log.\n\n```ruby\nt.fatal(\"fatal error!\")\n```\n\n### Rgot::Common#fatalf\n\nFatal logging same as logf\n\n### Rgot::Common#skip\n\n```ruby\nt.skip(\"this method was skipped\")\n```\n\nSkip current testing method.\n\nAnd run to next testing method.\n\n### Rgot::Common#skipf\n\nSkip logging same as logf\n\n## Rgot::T (Testing)\n\nTesting is a main usage of this package.\n\n```ruby\nmodule TestSomeCode\n  def test_some_1(t)\n  end\nend\n```\n\nThe `t` variable is instance of `Rgot::T` class means Testing.\n\n## Rgot::B (Benchmark)\n\nFor Benchmark class.\n\nCan use log methods same as `Rgot::T` class\n\n### Rgot::B#n\n\nAutomatic number calculated by running time.\n\nRecommend to this idiom.\n\n```ruby\ndef benchmark_something(b)\n  i = 0\n  while i \u003c b.n\n    something()\n    i += 1\n  end\nend\n```\n\n### Rgot::B#reset_timer\n\nReset benchmark timer\n\n```ruby\ndef benchmark_something(b)\n  obj = heavy_prepare_method()\n  b.reset_timer # you can ignore time of havy_prepare_method()\n  i = 0\n  while i \u003c b.n\n    obj.something()\n    i += 1\n  end\nend\n```\n\n### Rgot::B#start_timer\n\nStart benchmark timer\n\n### Rgot::B#stop_timer\n\nStop benchmark timer\n\n### Rgot::B#run_parallel\n\nStart parallel benchmark using `fork` and `Thread.new`.\n\nThis method should be call with block.\n\nThe block argument is instance of Rgot::PB.\n\n## Rgot::PB (Parallel Benchmark)\n\n### Rgot::PB#next\n\nShould be call this when parallel benchmark.\n\nRepeat while return false.\n\nRecommend this idiom.\n\n```ruby\ndef benchmark_foo(b)\n  b.run_parallel do |pb|\n    while pb.next\n      some_func()\n    end\n  end\nend\n```\n\n## Rgot::F (Fuzzing)\n\n### Rgot::F#add\n\nSet the sample value with `#add`. This value is also used as a test. It guesses the type from the value and generates a random value.\n\n### Rgot::F#fuzz\n\nGenerate the random value generated by `#fuzz` and execute the code.\nThe `t` becomes an instance of `Rgot::T` and the test can be run as usual.\n\n```ruby\ndef fuzz_foo(f)\n  f.add(100, \"hello\")\n  f.fuzz do |t, i, s|\n    i #=\u003e 100, 84, 17, 9, 66, ...\n    s #=\u003e \"hello\", \"Y\\xD5\\xAB\\xBA\\x8E\", \"r\\x95D\\xA5\\xF7\", \"\\xCEj=\\x9C\\xBD\", ...\n    if !foo(i, s)\n      t.error(\"fail with i=#{i}, s=#{s}\")\n    end\n  end\nend\n```\n\n# TODO\n\n- [ ] Support to save and load fuzzing data\n\n## v2\n\n- [ ] Support sub testing\n- [ ] Fix duration argument unit\n- [ ] Refactoring\n  - [ ] Fix M#initialize argument\n  - [ ] Fix internal class API\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksss%2Frgot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fksss%2Frgot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksss%2Frgot/lists"}