Git Product home page Git Product logo

rapidcheck's Introduction

RapidCheck Build Status Build status

RapidCheck is a C++ framework for property based testing inspired by QuickCheck and other similar frameworks. In property based testing, you state facts about your code that given certain precondition should always be true. RapidCheck then generates random test data to try and find a case for which the property doesn't hold. If such a case is found, RapidCheck tries to find the smallest case (for some definition of smallest) for which the property is still false and then displays this as a counterexample. For example, if the input is an integer, RapidCheck tries to find the smallest integer for which the property is false.

Sounds interesting? Why don't you read the User Guide to learn more!

Why RapidCheck?

There are existing implementations of property based testing but the ones that I have found are either (in my humble opinion) a bit clunky or are missing essential features such as test case shrinking.

Let's throw together a list of features:

  • Write your properties in an imperative way that makes sense for C++
  • Test case shrinking
  • Great support for STL types, including maps and sets
  • Advanced combinators for creating your own generators
  • Stateful based on commands in the vein of Erlang QuickCheck
  • Integration with popular testing frameworks such as Boost Test, Google Test and Google Mock

Prerequisites and installation

RapidCheck makes extensive use of C++11 and thus requires a compliant compiler. RapidCheck continuous integration builds using Clang 3.5, GCC 4.9 and Visual Studio 2015 so any later versions should also work.

RapidCheck uses CMake and is built like any other CMake project. If your own project uses CMake you can simply have RapidCheck as a subdirectory and add the following to your CMakeLists.txt:

add_subdirectory("path/to/rapidcheck")
target_link_libraries(my_target rapidcheck)

This will give you both linking and include directories.

Quick introduction

A common first example is testing a reversal function. For such a function, double reversal should always result in the original list. In this example we will use the standard C++ std::reverse function:

#include <rapidcheck.h>

#include <vector>
#include <algorithm>

int main() {
  rc::check("double reversal yields the original value",
            [](const std::vector<int> &l0) {
              auto l1 = l0;
              std::reverse(begin(l1), end(l1));
              std::reverse(begin(l1), end(l1));
              RC_ASSERT(l0 == l1);
            });

  return 0;
}

The check function is used to check properties. The first parameter is an optional string which describes the property. The second parameter is a callable object that implements the property, in this case a lambda. Any parameters to the callable (in our case the l0 parameter) will be randomly generated. The RC_ASSERT macro works just like any other assert macro. If the given condition is false, the property has been falsified.

The property above also forms part of a specification of the reversal function: "For any list of integers A, reversing and then reversing again should result in A".

If we were to run this, RapidCheck would (hopefully) output the following:

Using configuration: seed=9928307433081493900

- double reversal yields the original value
OK, passed 100 tests

Here, RapidCheck tells us that it ran 100 test cases and all of them passed. It also tells us the configuration that was used, in particular the random seed. If there was a bug in the implementation of std::reverse we could get the following output instead:

Falsifiable after 12 tests and 10 shrinks

std::tuple<std::vector<int>>:
([1, 0, 0, 0, 0, 0, 0, 0, 0, 0])

main.cpp:17:
RC_ASSERT(l0 == l1)

Expands to:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0] == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Here RapidCheck tells us that it found a case for which the property does not hold after running 12 tests. When it found this case, it shrunk it 10 times to arrive at the counterexample in the output. The counterexample contains each input value that was used for the failing case along with its type. Since RapidCheck views property arguments as tuples, the type is shown here as std::tuple<std::vector<int>>.

Can you guess what the bug is? The fact that there are exactly 10 items should give a clue. In this case, the bug is that the implementation sets the first element to 0 when l0.size() >= 10. This is also the reason for the initial 0, the problem doesn't manifest when all elements are zero. How did this bug happen? Who knows!

Thanks

Big thanks to my employer, Spotify, for making it possible for me to spend work time improving RapidCheck.

rapidcheck's People

Contributors

annius avatar cypher1 avatar dartdart26 avatar emil-e avatar ezzieyguywuf avatar furuholm avatar gpyalt avatar janisozaur avatar jnohlgard avatar jvanrhijn avatar m-pilia avatar mheasell avatar michelecicciottiwork avatar milleniumbug avatar mkloczko avatar nathompson avatar nikola-benes avatar objectx avatar parbo avatar per-gron avatar pyrrho avatar som1lse avatar stolyaroleh avatar thorius avatar timmmm avatar tocic avatar trofi avatar weonyuan avatar wverbeke avatar yairchu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rapidcheck's Issues

Parallel testing

Parallel testing is supported by QuickCheck and PropEr and I think it will prove very useful for RapidCheck as well.

I have created a proof of concept[1] and a gist that shows an example of usage[2]. This proof of concept follows the design of the QuickCheck [3] and PropEr[4] counterparts where possible. Note that this is a prototype and nothing else!

At least one API change is required for this feature to be implemented and that is that the behavior of the run function has to be split into a execution step and a verification step. This can be done by adding a new function to the Command class or by letting the run function return a callable or a std::function that can be used for verification at a later point. Returning a function might be the preffered way since the run function otherwise typically will have to change the state of its Command object (This is what the proof of concept does. I have even made a field mutable in order to minimize the changes necessary in the prototype).

Shrinking works given that enough retries are made. The count of retries has to be configurable in a complete solution.

[1] https://github.com/furuholm/rapidcheck/tree/parallel_testing
[2] https://gist.github.com/furuholm/bc98d94178c96b0f341a
[3] Finding Race Conditions in Erlang with QuickCheck and PULSE - http://www.cse.chalmers.se/~nicsma/papers/finding-race-conditions.pdf
[4] PropEr documentation of proper_statem module - http://proper.softlab.ntua.gr/doc/proper_statem.html

Option for more verbose output

For slow properties, it's useful to see some visual progress and how the test is going. Also, it might be useful to have different levels of verbosity?

Investigate rc::show performance

I see two options for improving the rc::show performance:

  • Ditch std::ostream and just return strings instead. This might be faster but that would have to be benchmarked.
  • Lazily construct counterexample. It's only needed for the final shrink, no need to create it always.

how to build ?

Would love some insight on the build process.
I'm an owner of clibs and would love to write up an install script for this when it is complete !

: ]

Improve formatting of counterexamples

Arguments are implemented as tuples and printed as such but this is perhaps an implementation detail that need not be shown. Some other format might be more readable.

Also, for a lot of types, the type itself is not very interesting. Command sequences are a good example, they are vectors of commands but that type is very long and not very interesting.

Some visual cues that show values are picked in sequence?

For a lot of these, it might be useful if you can customize not only the type and the value displaying separately but also change how the value and the type are displayed together.

Add shrinking to gen::inRange

gen::inRange should shrink to the lower bound. This is not optimal in all cases but I cannot see that it will mean trouble in any case.

Try removing some type erasure

Some of the type erasure in Shrinkable combinators and Seq combinators can perhaps be removed. However, it cannot be at the cost of increased compile time.

Omit empty argument tuple in counter example

If there are no arguments to a property function, the empty tuple should be emitted. It's confusing the way it is now.

Maybe a one argument tuple should be simplified to a non-tuple, as well?

Add gen::apply generator combinator

Example:

int f(int a, int b, int c);

// gen will have type Gen<int>
const auto gen = gen::apply(&f,
    gen::arbitrary<int>(),
    gen::arbitrary<int>(),
    gen::arbitrary<int>());

Track and publish code coverage

In the spirit of test-driven development, code coverage should be tracked and hosted somewhere. I suggest coveralls.io because it's familiar and pretty simple to integrate with travis.

Support VS2015

Visual Studio 2015 should have enough C++11 support that the support could be enabled with minimal changes to the source code.

Prettier display of std::chrono types

For example, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1l>> should print as std::chrono::seconds.

Also, there is no showType implementation and there should be.

Errors with 32 bit compiles

With MSVC, if you compile the project in win32 mode, it errors with several warnings. Is this a supported build? I did not notice appveyor testing this.

Parallelized test execution

Assuming there are no globals, you can most likely run multiple test cases in parallel. Also useful when searching for shrinks... gotta be a lot faster?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.