{"id":20092938,"url":"https://github.com/hedgehogqa/fsharp-hedgehog-experimental","last_synced_at":"2025-05-06T04:31:52.214Z","repository":{"id":43081842,"uuid":"108929878","full_name":"hedgehogqa/fsharp-hedgehog-experimental","owner":"hedgehogqa","description":"Hedgehog with batteries included: Auto-generators, extra combinators, and more.","archived":false,"fork":false,"pushed_at":"2025-04-27T03:18:59.000Z","size":481,"stargazers_count":24,"open_issues_count":4,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-27T03:31:46.954Z","etag":null,"topics":["combinators","hedgehog","property-based-testing","testing"],"latest_commit_sha":null,"homepage":"","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hedgehogqa.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"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":"2017-10-31T01:42:05.000Z","updated_at":"2025-04-27T03:18:14.000Z","dependencies_parsed_at":"2025-03-14T13:30:41.007Z","dependency_job_id":"4cecd59c-6555-4d46-8d8e-5d008d0aa9f6","html_url":"https://github.com/hedgehogqa/fsharp-hedgehog-experimental","commit_stats":null,"previous_names":["cmeeren/fsharp-hedgehog-experimental"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hedgehogqa%2Ffsharp-hedgehog-experimental","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hedgehogqa%2Ffsharp-hedgehog-experimental/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hedgehogqa%2Ffsharp-hedgehog-experimental/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hedgehogqa%2Ffsharp-hedgehog-experimental/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hedgehogqa","download_url":"https://codeload.github.com/hedgehogqa/fsharp-hedgehog-experimental/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252622173,"owners_count":21777932,"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":["combinators","hedgehog","property-based-testing","testing"],"created_at":"2024-11-13T16:45:19.240Z","updated_at":"2025-05-06T04:31:52.205Z","avatar_url":"https://github.com/hedgehogqa.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fsharp-hedgehog-experimental\n\n[![NuGet][nuget-shield]][nuget] [![AppVeyor][appveyor-shield]][appveyor]\n\n[Hedgehog][hedgehog] with batteries included: Auto-generators, extra combinators, and more.\n\n\u003cimg src=\"https://github.com/cmeeren/fsharp-hedgehog-experimental/raw/master/img/SQUARE_hedgehog_615x615.png\" width=\"307\" align=\"right\"/\u003e\n\n## Features\n\n- Auto-generation of arbitrary types\n- Generation of functions\n- Lots of convenient combinators\n\n## Examples\n\n### Convenience combinators\n\n**Generate lists without having to explicitly use `Range`:**\n\n```f#\nlet! exponentialList = Gen.bool |\u003e GenX.eList 1 5 // Same as Gen.list (Range.exponential 1 5)\nlet! linearList      = Gen.bool |\u003e GenX.lList 1 5 // Same as Gen.list (Range.linear 1 5)\nlet! constantList    = Gen.bool |\u003e GenX.cList 1 5 // Same as Gen.list (Range.constant 1 5)\n```\n\n**Generate strings without having to explicitly use `Range`:**\n\n```f#\nlet! exponentialStr = Gen.alpha |\u003e GenX.eString 1 5 // Same as Gen.string (Range.exponential 1 5)\nlet! linearStr      = Gen.alpha |\u003e GenX.lString 1 5 // Same as Gen.string (Range.linear 1 5)\nlet! constantStr    = Gen.alpha |\u003e GenX.cString 1 5 // Same as Gen.string (Range.constant 1 5)\n```\n\n**Generate a random shuffle/permutation of a list:**\n\n```f#\nlet lst = [1; 2; 3; 4; 5]\nlet! shuffled = GenX.shuffle lst // e.g. [2; 1; 5; 3; 4]\n```\n\n(Note that the shuffle may produce an identical list; this is more likely for shorter lists.)\n\n**Shuffle/permute the case of a string:**\n\n```f#\nlet str = \"abcde\"\nlet! shuffled = GenX.shuffleCase str // e.g. \"aBCdE\"\n```\n\n**Generate an element that is not equal to another element (direct or option-wrapped):**\n\n```f#\n// Generates an int that is not 0\nlet! notZero = Gen.int (Range.exponentialBounded()) |\u003e GenX.notEqualTo 0\n\n// Also generates an int that is not 0\nlet! notZero = Gen.int (Range.exponentialBounded()) |\u003e GenX.notEqualToOpt (Some 0)\n\n// Can generate any int\nlet! anyInt = Gen.int (Range.exponentialBounded()) |\u003e GenX.notEqualToOpt None\n```\n\n**Generate a string that does not equal another string, start with another string, or is a substring of another string:**\n\n```f#\nlet strGen = Gen.alpha |\u003e GenX.lString 1 5\n\nlet! ex1 = strGen |\u003e GenX.notEqualTo \"A\" // Does not generate \"A\" (same function as previous example)\nlet! ex2 = strGen |\u003e GenX.iNotEqualTo \"A\" // Case insensitive, does not generate \"A\" or \"a\"\nlet! ex3 = strGen |\u003e GenX.notSubstringOf \"fooBar\" // Does not generate e.g. \"Bar\" (but \"bar\" is OK)\nlet! ex4 = strGen |\u003e GenX.iNotSubstringOf \"fooBar\" // Case insensitive, does not generate e.g. \"Bar\" or bar\"\nlet! ex5 = strGen |\u003e GenX.notStartsWith \"foo\" // Does not generate e.g. \"foobar\" (but \"Foobar\" is OK)\nlet! ex6 = strGen |\u003e GenX.iNotStartsWith \"foo\" // Case insensitive, does not generate e.g. \"foobar\" or \"Foobar\"\n```\n\n**Generate an item that is not in a specified list:**\n\n```f#\nlet! str = Gen.int (Range.exponentialBounded()) |\u003e GenX.notIn [1; 2] // Does not generate 1 or 2\n```\n\n**Generate a list that does not contain a specified item:**\n\n```f#\n// Produces a list not containins 2, e.g. [1; 5; 7]. Note that this is a filter and not\n// a removal after the list has been generated, so the length of the list is unaffected.\nlet! intList = Gen.int (Range.exponentialBounded()) |\u003e GenX.cList 3 3 |\u003e GenX.notContains 2\n```\n\n**Generate a list that contains a specified item at a random index:**\n\n```f#\n// Generates a list and inserts the element. The list is 1 element longer than it would otherwise have been.\nlet! intList = Gen.int (Range.exponentialBounded()) |\u003e GenX.cList 3 3 |\u003e GenX.addElement 2\n```\n\n**Generate null some of the time, or don't generate nulls:**\n\n```f#\nlet strGen = Gen.alpha |\u003e GenX.eString 1 5\n\n// Generates null part of the time (same frequency as Gen.option generates None)\nlet nullStrGen = strGen |\u003e GenX.withNull\n\n// Does not generate null\nlet noNullStrGen = nullStrGen |\u003e GenX.noNull\n\n\n```\n\n**Generate sorted/distinct tuples (2, 3 or 4 elements):**\n\n```f#\n// Can produce 'a', 'a', 'c' but not 'a', 'c', 'b'\nlet! x, y, z = Gen.alpha |\u003e Gen.tuple3 |\u003e GenX.sorted3\n\n// Can produce 'b', 'a', 'c' but not 'b', 'b', 'c'\nlet! x, y, z = Gen.alpha |\u003e Gen.tuple3 |\u003e GenX.distinct3\n\n// Strictly increasing - can produce 'a', 'b', 'c' but not 'a', 'a', 'c'\nlet! x, y, z = Gen.alpha |\u003e Gen.tuple3 |\u003e GenX.increasing3\n```\n\n**Generate a date range:**\n\n```f#\n// Generates two dates at least 1 and at most 10 days apart, each with random time of day.\n// The interval increases linearly with the implicit size parameter.\nlet! d1, d2 = GenX.dateInterval (Range.linear 1 10)\n```\n\n**Generate a function:**\n\n```f#\n// Generates a list using inpGen together with a function that maps each of the distinct\n// elements in the list to values generated by outGen. Distinct elements in the input list\n// may map to the same output values. For example, [2; 3; 2] may map to ['A'; 'B'; 'A'] or\n// ['A'; 'A'; 'A'], but never ['A'; 'B'; 'C']. The generated function throws if called with\n// values not present in the input list.\nlet intGen = Gen.int (Range.exponentialBounded())\nlet charListGen = Gen.alpha |\u003e GenX.eList 1 10\nlet! chars, f = charListGen |\u003e GenX.withMapTo intGen\n// chars : char list\n// f : char -\u003e int\n\n\n// Generates a list using inpGen together with a function that maps each of the\n// distinct elements in the list to values generated by outGen. Distinct elements\n// in the input list are guaranteed to map to distinct output values. For example,\n// [2; 3; 2] may map to ['A'; 'B'; 'A'], but never ['A'; 'A'; 'A'] or ['A'; 'B'; 'C'].\n// Only use this if the output space is large enough that the required number of distinct\n// output values are likely to be generated. The generated function throws if called with\n// values not present in the input list.\nlet intGen = Gen.int (Range.exponentialBounded())\nlet charListGen = Gen.alpha |\u003e GenX.eList 1 10\nlet! chars, f = charListGen |\u003e GenX.withDistinctMapTo intGen\n// chars : char list\n// f : char -\u003e int\n```\n\n### Auto-generation\n\n**Generate any type automatically using default auto-generators for primitive types:**\n\n```f#\n// Can generate all F# types (unions, records, lists, etc.) as well as POCOs\n// with mutable properties or constructors.\n\ntype Union =\n  | Husband of int\n  | Wife of string\n\ntype Record =\n  {Sport: string\n   Time: TimeSpan}\n\n// Explicit type parameter may not be necessary if it can be inferred.\nlet! union = GenX.auto\u003cUnion\u003e\nlet! record = GenX.auto\u003cRecord\u003e\n\n// Recursive types are supported. By default, recurses at most once (subject to change).\ntype Recursive =\n  {OptChild: Recursive option\n   LstChild: Recursive list}\nlet! recursive = GenX.auto\u003cRecursive\u003e\n// E.g. {OptChild = Some {OptChild = None; LstChild = []}; LstChild = []}\n// Note that you may need to adjust the defaults when any kind of sequence is involved\n// (see below), since by default the range for generated sequences are Range.exponential 0 50\n// (subject to change).\n```\n\n**Generate any type automatically and override default generators and settings:**\n\n```f#\nlet! myVal =\n  GenX.defaults\n  |\u003e AutoGenConfig.setSeqRange (Range.exponential 1 10)\n  |\u003e AutoGenConfig.setRecursionDepth 2\n  // Will use this generator for all ints\n  |\u003e AutoGenConfig.addGenerator (Gen.int (Range.linear 0 10))\n  // Will use this generator when generating its return type\n  |\u003e AutoGenConfig.addGenerator Gen.myCustomGen\n  // Generate using the config above\n  |\u003e GenX.autoWith\u003cMyType\u003e\n```\n\nIf you’re not happy with the auto-gen defaults, you can of course create your own generator that calls `GenX.autoWith` with your chosen config and use that everywhere.\n\nDeployment checklist\n--------------------\n\nFor maintainers.\n\n- Make necessary changes to the code\n- Update the changelog\n- Update the version and release notes in the fsproj file (incrementing the version on `master` is what triggers the deployment to NuGet)\n- Commit and push\n\n\nEach commit to `master`\n\n[hedgehog]: https://github.com/hedgehogqa/fsharp-hedgehog\n\n[nuget]: https://www.nuget.org/packages/Hedgehog.Experimental/\n[nuget-shield]: https://img.shields.io/nuget/dt/Hedgehog.Experimental.svg?style=flat\n\n[appveyor]: https://ci.appveyor.com/project/cmeeren/fsharp-hedgehog-experimental/\n[appveyor-shield]: https://ci.appveyor.com/api/projects/status/9j83svr5wu0ydr23/branch/master?svg=true\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhedgehogqa%2Ffsharp-hedgehog-experimental","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhedgehogqa%2Ffsharp-hedgehog-experimental","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhedgehogqa%2Ffsharp-hedgehog-experimental/lists"}