-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RapidCheck Integration #24
Comments
If you don't mind waiting a couple weeks, I'll see if I can whip up some code (probably as a separate project) that you could use to glue mettle and RapidCheck together. I'm just finishing up a few loose ends before I release 1.0a1 of mettle, so after that would be a good time to experiment with integrating with other tools. If you want to look into doing this yourself, read on. Off the top of my head, I can think of two different ways to do this: first, you could write a wrapper function that takes a RapidCheck callback and does whatever magic is required to turn it into a callable that mettle understands. Something like this: // This should really be a function template that can take arguments for any fixtures...
std::function<void(void)>
wrap_rc(some_class callable) {
return [callable]() {
if (!run_rapidcheck(callable))
raise mettle::expectation_error(rapidcheck_msg);
};
}
// Somewhere inside a suite:
_.test("my rapidcheck test", wrap_rc([](const myclass &x) { /* ... */ })); What really matters in the above is that you spit out a function with the right arity and which raises a Another way to do it would be to create your own If you look in I would probably lean towards creating an |
Jim, thanks for the guidance. I will call out @emil-e on this message too. After many fits and starts, I have an example that looks pretty cool. My first version just created a compiled suite as you suggested. I kept beating on it and this is what I have now. First of all, this is what the suite looks like. It mixes both unit tests and property tests together. The new thing is the
Running this suite spews
which is great, because we get the normal mettle behavior but also the rapidcheck behavior also. For mettle infrastructure, there is only one class that is actually new. It provides exactly two things: the
So all of that looks pretty cool. The nasty part is punching the new
Maybe this last part is the right solution, or maybe we should pass the builder type as yet another template parameter. This current solution, probably, does not fully support all of mettle's features like fixtures, attributes, and subsuites. To get all of that, I would have to copy/paste/replace a lot more of So this experiment is pretty exciting. It has a really nice user interface modeled after |
Hmm, thinking about it more, it might be simpler to just expose _.test("my property test", wrap_property([](const std::vector<int>& l0) {
// ...
})); That makes it a lot easier to inject RapidCheck into mettle, since the only thing you should need is the For my implementation, I was actually thinking about making |
That said, I'm pretty stoked that things came together so well! Aside from the |
I don't have any particular opinion on how this fits in to mettle, of course, but when it comes to RapidCheck, it would be awesome if you could provide It seems like the |
Yeah, I am assuming that there will always be unsupported mettle features until every bit of Going forward to get all the features, it makes a big difference on how you decide to get the coverage inside of make_suite(). |
Emil, that is a good suggestion to get better coverage on RapidCheck features. I have not yet actually tried the reproducing feature, so I was not thinking about it. 8-) This sounds like a solvable problem. I used a lambda for To go forward, we kind of need a branch(es) to work on. I might let Jim mull the new pattern over for a few days first. If we get a new mettle branch from Jim that allows a builder subclass, then I'll use that and I will only have to fork RapidCheck and not mettle. Actually, there will be a RapidCheck fork in either case to add |
I'd be more than happy to merge in support for mettle if it seems appropriate (w.r.t. feature coverage, test coverage, code quality etc.). |
@JohnGalbraith getting back to this, are there any major issues with my suggestion here? You could provide some code like this: template <typename Testable>
auto property(Testable testable) {
return [testable]() {
// see above...
};
} And then use it like: mettle::suite<> test_properties("properties", [](auto &_) {
_.test("passing property", property([](const std::vector<int> &vec) {
// see above...
});
}); I like this way because it's easy for multiple third-parties to create their own wrapper functions like Granted, this way is a bit more verbose (you have to type I can definitely see an argument for allowing subclasses of suites that hold all-new kinds of things, but it feels to me like this is ultimately just an ordinary test that needs a metafunction to be called on the test function first. |
@JohnGalbraith Another option that would give RapidCheck properties (mostly) equal footing with regular mettle tests would be for me to add |
I am in total agreement about the pros and cons of making a property just a special kind of test vs. it's own non-test stature. It reads way better to say The free function idea would seem to improve the semantics somewhat and make it more clear that we are configuring a What about a pattern such as:
Which is kind of a middle ground, or even
to make unit tests, properties, and foobars all equal footing, plus easy to extend also.
which allows me to parameterize the property which is pretty cool, keeps clear semantics, plus I can add member functions in addition to |
Yeah, this is something I'm very conscious of and want to improve, not regress. :)
True; I'd love to eliminate it entirely, but that would involve a little more black magic than I'm comfortable with, and it would probably break some of the more-esoteric ways to work with mettle.
We can't actually do this, unfortunately, since
That would work, but implies that template <typename Builder, typename Testable>
auto property(Builder &_, std::string name, Testable testable) {
_.test(name, [testable]() {
// Do all the appropriate setup for a RapidCheck property. See
// `wrap_property()` above.
});
}
mettle::suite<...> my_root_suite("my root suite", [](auto &_) {
// `test()` is in the `mettle` namespace and should be picked up via ADL from
// the `_` argument. It has the same effect as `_.test()` and is provided as
// an alternate form for symmetry with `subsuite()` and third-party functions
// like `property()`.
test(_, "my basic test", []() {
// Do some testing here.
});
property(_, "my property", [](const std::vector<int> &vec) {
// Test the property here.
});
}); The benefit here is that all you have to write is the |
@JohnGalbraith I just noticed that I failed to "@" you in the previous comment. :/ Doing so here in case you missed it. |
Looking at the example, it really shows that using:
is really no more or less annoying that using:
One still has to figure out what the There might be a bit of trickiness if the Does the presence of attributes affect anything? Still, it seems like the free function can solve all the problems we have been discussing. |
That's a good point. It would probably be a good idea to add a little bit of "how the sausage is made" in the docs. (Some of this might be more obvious when I add concepts support, since the
This should be straightforward: template <typename Builder, typename Testable>
auto foobar(Builder &_, std::string name, Testable testable) {
// Accept any fixture parameters in our wrapper lambda, and then forward them
// onto our real function.
_.test(name, [testable](auto &...fixtures) {
std::string extra_arg = "from foobar()";
// Fixtures are always references, so I don't think we need to jump through
// the perfect-forwarding hoops here.
testable(extra_arg, fixtures...);
});
} In theory, you could also get the types of the fixtures from the suite builder (so that you're not saying
Attributes should only mean you need to have two overloads of @JohnGalbraith As I see it, there are basically three things I should fix in mettle here:
Does that sound reasonable? |
I was poking around the mettle source (of which I am no expert) to figure out how one might add a properties based test, in particular by calling the RapidCheck property library. The standard way in RapidCheck to call a test is
rc::check(test_name, callable)
which is tantalizingly close syntax to the way that mettle invokes a unit test.I thought that a good approach might be to extend mettle's
suite_builder<>
template with a new member function calledproperty()
, which would behave as close as possible totest()
, and would basically call the same property based test asrc::check()
, with the same arguments even.Now mettle is really generic and extendable, but I am not sure that augmenting
suite_builder<>
with a new member function in user code is easy or possible. It also makes no sense to add a dependency on RapidCheck. I think the correct way is to actually add the extension to RapidCheck itself, alongside similar features for boost::test and GoogleTest.Is there an easy and elegant way to get my
property()
member function alongsidetest()
, or is this hard?The text was updated successfully, but these errors were encountered: