-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add law testing guide #1880
Add law testing guide #1880
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1880 +/- ##
==========================================
+ Coverage 94.97% 95.17% +0.19%
==========================================
Files 241 248 +7
Lines 4199 4352 +153
Branches 106 126 +20
==========================================
+ Hits 3988 4142 +154
+ Misses 211 210 -1
Continue to review full report at Codecov.
|
@@ -0,0 +1,146 @@ | |||
# Law testing | |||
|
|||
Laws are an important part of cats. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Laws are explained in the last section in this type class page, maybe make that section a linkable anchor and link from here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Markdown embeds id
s so we don't even need an anchor 🎉. Great suggestion, will do!
Laws are an important part of cats. | ||
They help us enforce constraints that we usually assume to be true and give us guarantees that our code does what we think it does. | ||
Manually testing each data type to adhere to each of its type class instances can be quite cumbersome. | ||
Thankfully cats comes with the `cats-testkit`, which makes it a lot easier to write tests for type class instances using laws. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, strictly speaking, catalysts
and discipline
are the two libraries to make it easier to "write tests for type class instances using laws.", cats-testkit
is just a tiny lib on top of them to make it slightly more convenient to test against cats type classes using scalatests. relevant discussion https://gitter.im/typelevel/sbt-catalysts?at=59a85afdee5c9a4c5f1d45fb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true, what do you think of rewording to
"Thankfully cats comes with the cats-testkit
, which is based on catalysts
and discipline
, both of which make it a lot easier to write law tests for type class instances."
? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, it's probably more accurate to break it into two sentences.
catalysts and discipline are the two libraries to make it easier to "write tests for type class instances using laws. cats-testkit makes it more convenient to test against cats type classes using scalatests. paging @BennyHill for better wording.
|
||
```tut:invisible | ||
import org.scalacheck.{Arbitrary, Gen} | ||
implicit def arbFoo[A: Arbitrary]: Arbitrary[Tree[A]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this is the one that is actually in use, not the one generated by scalacheck-shapeless?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I realize it's not an optimal solution, but it's the best I came up with. If you have a better idea, please do tell :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think the spirit of our documentation is that people can just copy sample code to console and it runs. This breaks that assumption. why scalacheck-shapeless didn't work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't know how to include a foreign module for a tut doc 🤔. I guess it would be possible to add it as a dependency, but not sure if that's a good idea for just one part of the documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am inclined to not pretend to use it in the documentation. Make the arbitrary instance visible and add a note that you can use scalacheck-shapeless to auto derive this instance.
BTW, could we try avoid the asInstance
in the arbitrary instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got it to work by adding this single line to docs
in the build.sbt:
.settings(libraryDependencies += "com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.5" % Compile)
tut then compiles the whole thing without a hitch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still prefer making this visible and mentioned the alternative solution from scalacheck-shapeless
in a note.
|
||
[Laws](https://typelevel.org/cats/typeclasses.html#laws) are an important part of cats. | ||
They help us enforce constraints that we usually assume to be true and give us guarantees that our code does what we think it does. | ||
Manually testing each data type to adhere to each of its type class instances can be quite cumbersome. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, I think the wording here could use some polish. e.g. it's not testing data types, it's testing instances. I'd say for the sake of conciseness and time, how about we just say
[Laws](https://typelevel.org/cats/typeclasses.html#laws) are an important part of cats.
Cats uses catalysts and dicipline to help test instances with laws.
then introduce cats-testkit
Hey I just saw this and it looks great. I had to figure this out a few weeks ago and it was non-obvious so this will be very helpful for people. 👍 |
build.sbt
Outdated
@@ -181,7 +181,8 @@ lazy val docs = project | |||
.settings(noPublishSettings) | |||
.settings(docSettings) | |||
.settings(commonJvmSettings) | |||
.dependsOn(coreJVM, freeJVM) | |||
.settings(libraryDependencies += "com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.5" % Compile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would strongly discourage adding a third party dependency unless it is really essential. cats is quite low down the compile chain so the fewer dependencies it has that could get in the way of upgrades the better. And for thing like testing new scala compilers (eg 2.13.0.M1, native,etc) it is not the dependencies that a third party library brings into cats that is the issue, rather the dependencies needed to build that 3rd party library in the first place.
So I'm not saying it should not have any at all, but that we have to be careful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a starting suggestion/discussion.....
The same is true for testkit
, that I have suggested we split into two: a testkit
that has no dependencies other than pure cats, and another testkit-scalatest
that adds the scalatest dependency. The idea here is that we make the the base testkit independent to the testing frameworks. But what about specs2, should we also add a testkit-specs2
, testkit-utest
and so on?
And what about, say, cats-check. For sure we can't add that as a dependency.
So maybe now is a good time to address this. Maybe we need some kind of uber-cats repo that brings these things together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You raise a good point, I was hesitant to add the dependency before. I can revert the last commit and make the Arbitrary
instance explicit with a mention to scalacheck-shapeless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree we should be very cautious about adding dependencies to the project. In this particular case, it's a dependency added to the docs generation project, so no downstream impact. That's why I didn't object it. However, now I realized that it does impact the docs build in scala 2.13.
"Making the Arbitrary
instance explicit with a mention to scalacheck-shapeless" would make more sense to me.
Sorry for the tardiness of the comment, my bad |
Semi-unrelated to this commit but is there a reason why our Other than that, guide looks good! |
@adelbertc see #1272 |
checkAll("Tree[Int].MonoidLaws", GroupLaws[Tree[Int]].semigroup) | ||
checkAll("Tree.FunctorLaws", FunctorTests[Tree].functor[Int, Int, String]) | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that with catalysts you can do something similar to how algebra/kernel calls the laws, which is something like:
...
import catalysts.macros.TypeTagM
// this can be added to the general test suite
implicit def groupLaws[A: Eq: Arbitrary]: GroupLaws[A] = GroupLaws[A]
....
laws[GroupLaws, Tree[Int]].check(_.semigroup)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this is worth mentioning? I'm not really sure if I can see the benefit 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much @LukaJCB! This is something that we've needed for a long time. 🎉
Do we need to add this to https://github.com/typelevel/cats/blob/master/docs/src/main/resources/microsite/data/menu.yml and/or anywhere else? |
We may also want to link it from FAQ. |
Should resolve #1585. I'm thinking of creating another short section on why Laws are actually important, but for now this should be a good start I think.
Also I modified the build, so that
docs
depends ontestkit
andlaws
. Not sure, if this was the right way to do it, so feedback is very welcome :)