README.md in prop_check-0.15.0 vs README.md in prop_check-0.16.0

- old
+ new

@@ -1,13 +1,13 @@ # PropCheck PropCheck allows you to do Property Testing in Ruby. [![Gem](https://img.shields.io/gem/v/prop_check.svg)](https://rubygems.org/gems/prop_check) -[![Build Status](https://travis-ci.org/Qqwy/ruby-prop_check.svg?branch=master)](https://travis-ci.org/Qqwy/ruby-prop_check) +[![Ruby RSpec tests build status](https://github.com/Qqwy/ruby-prop_check/actions/workflows/run_tests.yaml/badge.svg)](https://github.com/Qqwy/ruby-prop_check/actions/workflows/run_tests.yaml) [![Maintainability](https://api.codeclimate.com/v1/badges/71897f5e6193a5124a53/maintainability)](https://codeclimate.com/github/Qqwy/ruby-prop_check/maintainability) -[![RubyDoc](https://img.shields.io/badge/%F0%9F%93%9ARubyDoc-documentation-informational.svg)](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck) +[![RubyDoc](https://img.shields.io/badge/%F0%9F%93%9ARubyDoc-documentation-informational.svg)](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/) It features: - Generators for most common Ruby datatypes. - An easy DSL to define your own generators (by combining existing ones, as well as completely custom ones). @@ -59,15 +59,15 @@ - [x] Good, unicode-compliant, string generators. - [x] Filtering generator outputs. - [x] Before/after/around hooks to add setup/teardown logic to be called before/after/around each time a check is run with new data. - [x] Possibility to resize generators. - [x] `#instance` generator to allow the easy creation of generators for custom datatypes. -- [ ] A usage guide. -- [ ] A simple way to create recursive generators - [x] Builtin generation of `Set`s - [x] Builtin generation of `Date`s, `Time`s and `DateTime`s. -- [ ] Configuration option to resize all generators given to a particular Property instance. +- [x] Configuration option to resize all generators given to a particular Property instance. +- [ ] A simple way to create recursive generators +- [ ] A usage guide. ## Nice-to-haves - Stateful property testing. If implemented at some point, will probably happen in a separate add-on library. @@ -98,13 +98,13 @@ The value(s) generated from the generator(s) passed to the `forall` will be given to the block as arguments. Raise an exception from the block if there is a problem. If there is no problem, just return normally. ```ruby -include PropCheck::Generators +G = PropCheck::Generators # testing that Enumerable#sort sorts in ascending order -PropCheck.forall(array(integer)) do |numbers| +PropCheck.forall(G.array(G.integer)) do |numbers| sorted_numbers = numbers.sort # Check that no number is smaller than the previous number sorted_numbers.each_cons(2) do |former, latter| raise "Elements are not sorted! #{latter} is < #{former}" if latter < former @@ -122,25 +122,25 @@ array.sum / array.length end ``` ```ruby # And then in a test case: -include PropCheck::Generators -PropCheck.forall(numbers: array(integer)) do |numbers:| +G = PropCheck::Generators +PropCheck.forall(numbers: G.array(G.integer)) do |numbers:| result = naive_average(numbers) unless result.is_a?(Integer) do raise "Expected the average to be an integer!" end end # Or if you e.g. are using RSpec: describe "#naive_average" do include PropCheck - include PropCheck::Generators + G = PropCheck::Generators it "returns an integer for any input" do - forall(numbers: array(integer)) do |numbers:| + forall(numbers: G.array(G.integer)) do |numbers:| result = naive_average(numbers) expect(result).to be_a(Integer) end end end @@ -206,15 +206,16 @@ It contains generators for: - (any, positive, negative, etc.) integers, - (any, only real-valued) floats, - (any, printable only, alphanumeric only, etc) strings and symbols - fixed-size arrays and hashes -- as well as varying-size arrays and hashes. +- as well as varying-size arrays, hashes and sets. +- dates, times, datetimes. - and many more! -It is common to call `include PropCheck::Generators` in e.g. your testing-suite files to be able to use these. -If you want to be more explicit (but somewhat more verbose) when calling these functions. feel free to e.g. create a module-alias (like `PG = PropCheck::Generators`) instead. +It is common and recommended to set up a module alias by using `G = PropCheck::Generators` in e.g. your testing-suite files to be able to refer to all of them. +_(Earlier versions of the library recommended including the module instead. But this will make it very simple to accidentally shadow a generator with a local variable named `float` or `array` and similar.)_ ### Writing Custom Generators As described in the previous section, PropCheck already comes bundled with a bunch of common generators. @@ -226,36 +227,40 @@ #### Generator#map Allows you to take the result of one generator and transform it into something else. - >> Generators.choose(32..128).map(&:chr).call(10, Random.new(42)) - => "S" + >> G.choose(32..128).map(&:chr).sample(1, size: 10, Random.new(42)) + => ["S"] #### Generator#bind Allows you to create one or another generator conditionally on the output of another generator. - >> Generators.integer.bind { |a| Generators.integer.bind { |b| Generator.wrap([a , b]) } }.call(100, Random.new(42)) - => [2, 79] + >> G.integer.bind { |a| G.integer.bind { |b| G.constant([a , b]) } }.sample(1, size: 100, rng: Random.new(42) + => [[2, 79]] +This is an advanced feature. Often, you can use a combination of `Generators.tuple` and `Generator#map` instead: + >> G.tuple(integer, integer).sample(1, size: 100, rng: Random.new(42) + => [[2, 79]] + #### Generators.one_of Useful if you want to be able to generate a value to be one of multiple possibilities: - >> Generators.one_of(Generators.constant(true), Generators.constant(false)).sample(5, size: 10, rng: Random.new(42)) + >> G.one_of(G.constant(true), G.constant(false)).sample(5, size: 10, rng: Random.new(42)) => [true, false, true, true, true] -(note that for this example, you can also use `Generators.boolean`. The example happens to show how it is implemented under the hood.) +(Note that for this example, you can also use `G.boolean`. The example happens to show how it is implemented under the hood.) #### Generators.frequency If `one_of` does not give you enough flexibility because you want some results to be more common than others, you can use `Generators.frequency` which takes a hash of (integer_frequency => generator) keypairs. - >> Generators.frequency(5 => Generators.integer, 1 => Generators.printable_ascii_char).sample(size: 10, rng: Random.new(42)) + >> G.frequency(5 => G.integer, 1 => G.printable_ascii_char).sample(size: 10, rng: Random.new(42)) => [4, -3, 10, 8, 0, -7, 10, 1, "E", 10] #### Others There are even more functions in the `Generator` class and the `Generators` module that you might want to use,