Skip to content

Instantly share code, notes, and snippets.

@magicly
Created June 1, 2015 07:40
Show Gist options
  • Save magicly/77c40bc3a4b5483ad8cc to your computer and use it in GitHub Desktop.
Save magicly/77c40bc3a4b5483ad8cc to your computer and use it in GitHub Desktop.
intro to scalacheck
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="keywords" content="property-based testing, scalacheck, scala, java, unit testing, jUnit" />
<meta name="description" content="Property-based testing with ScalaCheck." />
<title>Remark</title>
<style type="text/css">
@import url(http://fonts.googleapis.com/css?family=Droid+Serif);
@import url(http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
@import url(http://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);
body {
font-family: 'Droid Serif';
}
h1, h2, h3 {
font-family: 'Yanone Kaffeesatz';
font-weight: 400;
margin-bottom: 0;
}
.remark-slide-content h1 { font-size: 3em; }
.remark-slide-content h2 { font-size: 2em; }
.remark-slide-content h3 { font-size: 1.6em; }
.footnote {
position: absolute;
bottom: 3em;
}
li p { line-height: 1.25em; }
.red { color: #fa0000; }
.large { font-size: 2em; }
a, a > code {
color: rgb(249, 38, 114);
text-decoration: none;
}
code {
-moz-border-radius: 5px;
-web-border-radius: 5px;
background: #e7e8e2;
border-radius: 5px;
}
.remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
.remark-code-line-highlighted { background-color: #373832; }
.pull-left {
float: left;
width: 47%;
}
.pull-right {
float: right;
width: 47%;
}
.pull-right ~ p {
clear: both;
}
#slideshow .slide .content code {
font-size: 0.8em;
}
#slideshow .slide .content pre code {
font-size: 0.9em;
padding: 15px;
}
.inverse {
background: #272822;
color: #777872;
text-shadow: 0 0 20px #333;
}
.inverse h1, .inverse h2 {
color: #f3f3f3;
line-height: 0.8em;
}
/* Slide-specific styling */
#slide-inverse .footnote {
bottom: 12px;
left: 20px;
}
#slide-how .slides {
font-size: 0.9em;
position: absolute;
top: 151px;
right: 140px;
}
#slide-how .slides h3 {
margin-top: 0.2em;
}
#slide-how .slides .first, #slide-how .slides .second {
padding: 1px 20px;
height: 90px;
width: 120px;
-moz-box-shadow: 0 0 10px #777;
-webkit-box-shadow: 0 0 10px #777;
box-shadow: 0 0 10px #777;
}
#slide-how .slides .first {
background: #fff;
position: absolute;
top: 20%;
left: 20%;
z-index: 1;
}
#slide-how .slides .second {
position: relative;
background: #fff;
z-index: 0;
}
/* Two-column layout */
.left-column {
color: #777;
width: 20%;
height: 92%;
float: left;
}
.left-column h2:last-of-type, .left-column h3:last-child {
color: #000;
}
.right-column {
width: 75%;
float: right;
padding-top: 1em;
}
.image {
padding: 0px;
color: white;
background-color: black;
position: relative;
}
.image img {
position: absolute;
top: 0;
left: 0;
width: 100%;
/*height: 100%;*/
float: left;
}
.image h1, .image h2 {
position: absolute;
bottom: 0;
text-align: center;
left: 20px;
}
.image-white {
color: black;
background-color: white;
}
.image-last h1 {
right: 0;
bottom: 100px;
}
</style>
</head>
<body>
<textarea id="source">
name: image
layout: true
class: center, middle, image
---
name: inverse
layout: true
class: center, middle, inverse
---
<img src="images/love-the-bomb.jpg" height="50%" />
#Property-based testing with ScalaCheck
---
## Test
---
layout: false
.left-column[
## Test
### Why?
]
.right-column[
- find bug
- quality
- ......
]
---
.left-column[
## Test
### Why?
### What?
]
.right-column[
- white box
* Unit
* integration
* .red[**Property-based**]
* ...
- black box
* user
* .red[**QA**]
* ...
]
---
template: inverse
## Unit Testing vs Property-based Testing
---
.left-column[
## Unit Testing
]
.right-column[
- [wiki](http://en.wikipedia.org/wiki/Unit_testing)
In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.
]
---
.left-column[
## Unit Testing
]
.right-column[
max
```Java
System.out.println(max(1, 2) == 2);
```
```Java
assert max(1, 2) == 2;
```
[assert trap!](http://lavasoft.blog.51cto.com/62575/43735)
```bash
java -ea MathUtilTest
```
]
---
.left-column[
## Unit Testing
]
.right-column[
- Framework
* Hibernate to ORM
* jQuery to js
* Taf
* ...
- jUnit
```Java
@Test
public void testMax() {
int z = MathUtil.max(1, 2);
assertEquals(2, z);
}
```
]
---
.left-column[
## Unit Testing
]
.right-column[
- jUnit
```Java
@Test
public void testMax2() {
int z = MathUtil.max(2, 1);
assertEquals(2, z);
}
@Test
public void testMax3() {
int z = MathUtil.max(2, 2);
assertEquals(2, z);
}
@Test
public void testMax4() {
int z = MathUtil.max(2, -1);
assertEquals(2, z);
}
```
Passed. yeah...
]
---
template: image
<img src="images/manual-labour.jpg" />
# Manual labour
---
template: inverse
<img src="images/jx.jpg" height="100%" />
# Are you sure ok?
---
.left-column[
## Unit Testing
]
.right-column[
```Java
System.out.println(MathUtil.max(-2, 1));// 2!!!!
```
Failed! oh, no...
]
---
template: inverse
<img src="images/whats_wrong.jpg" height="100%" />
---
template: inverse
#[writing the minimal code that will make the test pass](http://www.typemock.com/test-driven-development-tdd/)
---
class: middle
```Java
public static int max(int a, int b) {
return 2;
}
```
---
template: image
<img src="images/wtf2.jpg" height="100%" />
---
template: inverse
##[The Enterprise Developer From Hell, or "EDFH"](http://fsharpforfunandprofit.com/posts/property-based-testing/)
a burned-out, always lazy and often malicious programmer
---
template: inverse
#More test case?
---
class: middle
```Java
@Test
public void testMax2() {
int z = MathUtil.max(-2, 1);
assertEquals(1, z);
}
```
---
##hehe...
```Java
public static int max(int a, int b) {
if (a < 0)
return b;
return 2;
}
```
---
template: image
<img src="images/jx2.jpg" height="100%" />
---
template: image
<img src="images/AreYouKidding.jpg" height="100%" />
---
template: inverse
# How to fix?
---
template: inverse
# No Magic Number!
---
template: inverse
# Random!
---
class: middle
# Expected Value?
```Java
@Test
public void testMax2() {
Random r = new Random();
int a = r.nextInt();
int b = r.nextInt();
int max = MathUtil.max(a, b);
* assertEquals(?, max);
}
```
---
class: middle
# Expected Value?
```Java
@Test
public void testMax2() {
Random r = new Random();
int a = r.nextInt();
int b = r.nextInt();
int max = MathUtil.max(a, b);
int expectedValue = b;
if (a > b) {
expectedValue = a;
}
assertEquals(expectedValue, max);
}
```
---
# Duplicate
## [DRY(Don't repeat yourself)](http://en.wikipedia.org/wiki/Don't_repeat_yourself)
A bad idea to have your tests duplicate the code that you are testing!
## [Code smell](http://en.wikipedia.org/wiki/Code_smell)
In computer programming, code smell is any symptom in the source code of a program that possibly indicates a deeper problem.
---
template: image
<img src="images/duplicate_code.jpg" height="100%" />
---
template: image
<img src="images/duplicate_code2.jpg" height="100%" />
---
template: inverse
# [Property](http://en.wikipedia.org/wiki/Property)
In the abstract, property is that which belongs to or with something, whether as an .red[**attribute**] or as a .red[**component**] of said thing.
---
.left-column[
## Property demos
]
.right-column[
- a: Int + b: Int > a
- .red[a: Int + 1 > a]
- list.reverse.length == list.length
- list.sort.sort == list.sort
- (a + b).startsWith(a)
- (a + b).length > a.length && (a + b).length > b.length
- .red[s.toUpperCase.toLowerCase == s.toLowerCase]
- .red[new String(s.getBytes(c), c) == s]
- s.substring(0, n) + s.substring(n, s.length) == s
- .red[quickSort(array) == bubbleSort(array)(duplicate?)]
- (i :: l).size == (l.size + 1)
- ...
]
???
- b < 0
- overflow
- ""
- unicode, codec
- 因为是random所以不是必现,出现很少的fail,用UT单独记录下来保存
---
.left-column[
## Property based testing
]
.right-column[
- Focus on the properties of the function -- the "requirements"
These properties should be things that are true for any correct implementation.
- Different from min, add, sub?
* max(a, b) == max(b, a)
* max(a, b) > a || max(a, b) > b
* max(a, b) >= a && max(a, b) >= b
* max(a, b) == a || max(a, b) == b
* ...
]
???
- !(max(1, 1) > 1)
- max(a, b) = Int.MaxValue
---
.left-column[
## Property based testing
]
.right-column[
```Java
@Test
public void testMaxIsBiggerThanBoth() {
Random r = new Random();
for (int i = 0; i < 100; i++) {
int a = r.nextInt();
int b = r.nextInt();
int max = max(a, b);
System.out.printf("a=%d, b=%d, max=%d", a, b, max);
assert max >= a && max >= b;
}
}
@Test
public void testMaxIsOneOf() {
Random r = new Random();
for (int i = 0; i < 100; i++) {
int a = r.nextInt();
int b = r.nextInt();
int max = max(a, b);
System.out.printf("a=%d, b=%d, max=%d", a, b, max);
assert max == a || max == b;
}
}
```
]
---
### Refactor code
- How to pass code?
- Function as value
- Single Abstract Method interfaces
- Lambda
---
### Refactor code
```Java
interface Function4 { public boolean apply(int a, int b, int max, int max2); }
public void testAll(Function4 f) {
Random r = new Random();
for (int i = 0; i < 100; i++) {
int a = r.nextInt();
int b = r.nextInt();
int max = max(a, b);
int max2 = max(b, a);
System.out.printf("a=%d, b=%d, max=%d, max2=%d", a, b, max, max2);
assert f.apply(a, b, max, max2);
}
}
@Test
public void testMaxIsBiggerThanBothWithTestAll() {
testAll(new Function4() {
@Override
public boolean apply(int a, int b, int max, int max2) {
return max >= a && max >= b;
}
});
}
@Test
public void testMaxIsOneOfWithTestAll() {
testAll(new Function4() {
@Override
public boolean apply(int a, int b, int max, int max2) {
return max == a || max == b;
}
});
}
```
---
.left-column[
## Property based testing
]
.right-column[
### Do it better
- only integer
- 4 parameters, 2 no need
- what's wrong?
- 100 randoms? no config
- ...
]
---
.left-column[
## Property based testing
]
.right-column[
### Framework to rescue
* jUnit to UT
* Hibernate to ORM
* jQuery to js
* Taf
* ...
]
---
template: inverse
## ScalaCheck
---
# Property based testing frameworks
- [Haskell: QuickCheck](https://hackage.haskell.org/package/QuickCheck)
- [Scala: ScalaCheck](http://scalacheck.org/)
- [C++: CppQuickCheck](https://github.com/grogers0/CppQuickCheck)
- [Javascript: jsverify](https://github.com/jsverify/jsverify)
- [Erlang: proper](https://github.com/manopapad/proper)
- [Java: junit-quickcheck](https://github.com/pholser/junit-quickcheck)
- [Php: eris](https://github.com/giorgiosironi/eris)
- google: XXXLanguage property based testing...
---
class: center, middle
# ScalaCheck
#.red[Generate -> Run(Property) -> Shrink]
---
### Gen
- Gen.choose
```Scala
Gen.choose(1, 100)
```
- oneOf
```Scala
Gen.oneOf(1, 3, 6)
```
- frequency
```Scala
Gen.frequency((1, 'a'), (2, 'b'))
```
- gen.sample
- combination
```Scala
val myGen2 = for {
n <- choose(1, 50)
m <- choose(n, 2 * n)
} yield (n, m)
myGen2.sample
```
---
### Gen
- custom type
```Scala
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Gen.{oneOf, const}
sealed trait Tree
case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree
val genLeaf: Gen[Tree] = const(Leaf)
val genNode: Gen[Node] = for {
v <- arbitrary[Int]
left <- genTree
right <- genTree
} yield Node(left, right, v)
def genTree = oneOf(genLeaf, genNode)
genTree.sample
```
---
### Gen
- statistics
* classify
```Scala
import org.scalacheck.Prop._
def ordered(list: List[Int]) = list == list.sorted
val prop1 = forAll { list: List[Int] =>
classify(ordered(list), "ordered") {
classify(list.length > 5, "large", "small") {
list.reverse.reverse == list
}
}
}
prop1.check
```
---
### Property
- forAll
- p.check
```Scala
forAll {(a: Int, b: Int) =>
val max = MathUtil.max(a, b)
max >= a || max >= b
}.check
```
---
### Property
- combination
* &&, all
* ||, atLeastOne
* ==
```Scala
val p1 = forAll {(a: Int, b: Int) =>
val max = MathUtil.max(a, b)
max >= a || max >= b
}
val p2 = forAll {(a: Int, b: Int) =>
val max = MathUtil.max(a, b)
max >= a || max >= b
}
(p1 && p2).check
```
---
### Property
- conditional
* ==>
* condition is hard or impossible to fulfill
* pass, fail, undecided
```Scala
forAll { n: Int =>
n + 1 > n
}.check
forAll { n: Int =>
(n != Int.MaxValue) ==> {
n + 1 > n
}
}.check
```
---
### Property Pattern
next
---
### Shrink
- forAll, forAllNoShrink
```Scala
forAll { list: List[Int] =>
list.sorted == list
}.check
forAllNoShrink { list: List[Int] =>
list.sorted == list
}.check
```
```Scala
implicitly[Shrink[Int]].shrink(100).force
implicitly[Shrink[String]].shrink("adfe").force
implicitly[Shrink[(String, Int)]].shrink(("adfe", 5)).force
```
---
### Config
```Scala
def myParameter = new Parameters.Default {
override val minSuccessfulTests = 200
override val workers = 5
}
p1.check(myParameter)
```
---
### More
- Statefull
* Commands
- Database
- Network
- Concurrency
- ...
---
### Good
- More abstract
- More general, Test coverage
* Radom
* Corner case
- More concise
- Test readability
- Maintenance
- Test case simplification
- .red[**Property-based tests force you to think!**]
- .red[**Property-based tests force you to have a clean design!**]
---
template: inverse
## FAQ
---
## Refs
- [http://scalacheck.org/documentation.html](http://scalacheck.org/documentation.html)
- [http://blog.charleso.org/property-testing-preso/#1](http://blog.charleso.org/property-testing-preso/#1)
- [http://fsharpforfunandprofit.com/posts/property-based-testing-2/](http://fsharpforfunandprofit.com/posts/property-based-testing-2/)
- [http://www.amazon.com/ScalaCheck-Definitive-Guide-Rickard-Nilsson/dp/0981531695](http://www.amazon.com/ScalaCheck-Definitive-Guide-Rickard-Nilsson/dp/0981531695)
- [https://github.com/rickynils/scalacheck/wiki/User-Guide](https://github.com/rickynils/scalacheck/wiki/User-Guide)
</textarea>
<script src="remark-latest.min.js"></script>
<script type="text/javascript">
var hljs = remark.highlighter.engine;
</script>
<script src="remark.language.js" type="text/javascript"></script>
<script type="text/javascript">
var slideshow = remark.create({
highlightStyle: 'monokai',
highlightLanguage: 'remark'
}) ;
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment