├── .gitignore
├── README.txt
├── java-hadoop-scalacheck
├── .gitignore
├── README.txt
├── input
│ └── test.txt
├── pom.xml
├── runwordcount.sh
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── company
│ │ └── hadoop
│ │ └── WordCount.java
│ └── test
│ └── scala
│ └── MapperReducerSpecification.scala
├── java-scalacheck
├── README.txt
├── pom.xml
└── src
│ ├── main
│ └── com
│ │ └── company
│ │ └── account
│ │ ├── Account.java
│ │ └── InsufficientFundsException.java
│ └── test
│ └── com
│ └── company
│ └── test
│ ├── AccountSpecification.scala
│ └── AccountTest.java
├── scalacheck-basic
├── README.txt
├── pom.xml
└── src
│ ├── main
│ └── scala
│ │ └── Rectangle.scala
│ └── test
│ └── scala
│ ├── DataCollection.scala
│ ├── Generators.scala
│ ├── Runner.scala
│ └── SimpleSpecification.scala
├── scalacheck-integration-junit
├── README.txt
├── pom.xml
└── src
│ ├── main
│ └── scala
│ │ └── Rectangle.scala
│ └── test
│ └── scala
│ ├── BasicScalaJUnitTest.scala
│ ├── ScalaCheckJUnit.scala
│ ├── ScalaCheckJUnitRunnerExample.scala
│ └── ScalaCheckJunitSupportExample.scala
├── scalacheck-integration-scalatest
├── .gitignore
├── README.txt
├── build.sbt
├── project
│ └── build.properties
└── src
│ └── test
│ └── scala
│ ├── ArbitrarySpec.scala
│ └── SimpleSpec.scala
└── scalacheck-integration-specs
├── .gitignore
├── README.txt
├── build.sbt
├── project
└── build.properties
└── src
└── test
└── scala
├── ArbitrarySpec.scala
└── SimpleSpec.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /project/boot/
3 | /project/plugins/project
4 | target/
5 | lib_managed/
6 | src_managed/
7 | test-output/
8 | *.iml
9 | *.db
10 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | Example code used in the ScalaCheck cookbook deliverable.
2 |
3 | All the code used in this document is currently available in the Innersource repository: http://www.slideshare.net/oscarrenalias/scalacheck-cookbook-v10.
4 |
5 | The code is structured in folders based on the same structure used throughout this document. Examples where Java and Scala code are mixed (scalacheck-basic, java-scalacheck, scalacheck-integration-junit) require Maven 2.x or 3.x to build and run. Pure Scala examples (scalacheck-integration-specs, scalacheck-integration-scalatest) require Simple Build Tool version 0.11 or greater.
6 |
7 | There is no top-level project at the root folder above the sub-folders, so each project is an independent unit by itself.
8 |
9 | scalacheck-basic
10 | ----------------
11 | This folder contains basic ScalaCheck examples, showing most of its basic features (basic property checks, data grouping, conditional properties).
12 |
13 | In order to run, switch to the project�s subfolder and execute following command:
14 |
15 | mvn scala:run -Dlauncher=test
16 |
17 | java-scalacheck
18 | ---------------
19 | Integration of ScalaCheck property checks with Java code
20 |
21 | In order to run, switch to the project�s subfolder and execute following command:
22 |
23 | mvn scala:run -Dlauncher=test
24 |
25 | scalacheck-integration-scalatest
26 | --------------------------------
27 | Examples of integration of ScalaTest with with ScalaCheck. Requires SBT.
28 |
29 | In order to run, switch to the project�s subfolder and execute following command:
30 |
31 | sbt reload test
32 |
33 | scalacheck-integration-specs
34 | ----------------------------
35 | Examples of integration of Specs with ScalaCheck. Requires SBT.
36 |
37 | In order to run, switch to the project�s subfolder and execute following command:
38 |
39 | sbt reload test
40 |
41 | scalacheck-integration-junit
42 | ----------------------------
43 | Examples of integration of ScalaCheck with JUnit, as well as JUnit support traits and a JUnit 4 runner for ScalaCheck tests written as Properties classes.
44 |
45 | This example uses the Maven JUnit test runner (Surefire plugin) to run.
46 |
47 | In order to run, switch to the project�s subfolder and execute following command:
48 |
49 | mvn test
50 |
51 | java-hadoop-scalacheck
52 | ----------------------
53 | Code and ScalaCheck test cases that test Hadoop�s WordCount example.
54 |
55 | In order to run, switch to the project�s subfolder and execute following command:
56 |
57 | mvn scala:run -Dlauncher=test
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /project/boot/
3 | /project/plugins/project
4 | target/
5 | lib_managed/
6 | src_managed/
7 | test-output/
8 | *.iml
9 | *.db
10 | .DS_Store
11 | output/
12 |
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/README.txt:
--------------------------------------------------------------------------------
1 | This class contains an example Map Reduce program implemented on Hadoop (based on the classic WordCount) and some example ScalaCheck generators for testing
2 | the mapper and the reducer using random data.
3 |
4 | Required libraries are:
5 |
6 | Hadoop 0.20
7 | Cloudera Hadoop MrUnit 0.20.2-737
8 |
9 | The code is built with Maven, and the properties can be run as follows:
10 |
11 | mvn scala:run -Dlauncher=test
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.company.scalacheck.hadoop
8 | Java-Hadoop-ScalaCheck
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | scala-tools.org
14 | Scala-tools Maven2 Repository
15 | http://scala-tools.org/repo-releases
16 |
17 |
18 | releases
19 | Cloudera Releases
20 | https://repository.cloudera.com/content/repositories/releases
21 |
22 |
23 | maven
24 | Maven
25 | http://repo2.maven.org/maven2
26 |
27 |
28 |
29 |
30 |
31 | scala-tools.org
32 | Scala-tools Maven2 Repository
33 | http://scala-tools.org/repo-releases
34 |
35 |
36 |
37 |
38 | 2.9.0
39 | UTF-8
40 |
41 |
42 |
43 |
44 |
45 | org.scala-lang
46 | scala-library
47 | ${scala.version}
48 |
49 |
50 | org.apache.hadoop
51 | hadoop-core
52 | 0.20.2
53 |
54 |
55 |
56 | com.cloudera.hadoop
57 | hadoop-mrunit
58 | 0.20.2-737
59 | test
60 |
61 |
62 | org.scala-tools.testing
63 | specs_2.9.0-1
64 | 1.6.8
65 | test
66 |
67 |
68 | junit
69 | junit
70 | 4.8.2
71 | test
72 |
73 |
74 | org.scala-lang
75 | jline
76 | 2.9.0-1
77 |
78 |
79 | org.scala-tools.testing
80 | scalacheck_2.9.0-1
81 | 1.9
82 |
83 |
84 |
85 |
86 |
87 | org.scala-tools
88 | maven-scala-plugin
89 |
90 |
91 | compile
92 |
93 | compile
94 |
95 | compile
96 |
97 |
98 | test-compile
99 |
100 | testCompile
101 |
102 | test-compile
103 |
104 |
105 |
106 | ${scala.version}
107 |
108 | -Xss1m
109 |
110 |
111 |
112 |
113 | test
114 | com.company.hadoop.tests.TestRunner
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | org.apache.maven.plugins
123 | maven-compiler-plugin
124 |
125 | 1.6
126 | 1.6
127 |
128 |
129 |
130 |
131 | org.apache.maven.plugins
132 | maven-surefire-plugin
133 |
134 | false
135 | -Xmx512m
136 |
137 | **/*Unit.java
138 | **/*Spec.java
139 |
140 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/runwordcount.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | mvn clean package
3 | rm -rf output
4 | hadoop jar target/Java-Hadoop-ScalaCheck-1.0-SNAPSHOT.jar com.company.hadoop.WordCount input output
5 |
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/src/main/java/com/company/hadoop/WordCount.java:
--------------------------------------------------------------------------------
1 | package com.company.hadoop;
2 |
3 | import org.apache.hadoop.fs.Path;
4 | import org.apache.hadoop.io.IntWritable;
5 | import org.apache.hadoop.io.LongWritable;
6 | import org.apache.hadoop.io.Text;
7 | import org.apache.hadoop.mapreduce.Job;
8 | import org.apache.hadoop.mapreduce.Mapper;
9 | import org.apache.hadoop.mapreduce.Reducer;
10 | import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
11 | import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
12 |
13 | import java.io.IOException;
14 | import java.util.StringTokenizer;
15 |
16 | public class WordCount {
17 |
18 | public static class Map extends Mapper {
19 | private final static IntWritable one = new IntWritable(1);
20 | private Text word = new Text();
21 |
22 | public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
23 | String line = value.toString();
24 | StringTokenizer tokenizer = new StringTokenizer(line);
25 | while (tokenizer.hasMoreTokens()) {
26 | word.set(tokenizer.nextToken());
27 | context.write(word, one);
28 | }
29 | }
30 |
31 | }
32 |
33 | public static class Reduce extends Reducer {
34 |
35 | public void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {
36 | // do nothing for empty keys
37 | if(key.getLength() == 0)
38 | return;
39 |
40 | // otherwise sump up the values
41 | int sum = 0;
42 | for (IntWritable value : values) {
43 | sum += value.get();
44 | }
45 | context.write(key, new IntWritable(sum));
46 | }
47 | }
48 |
49 | public static void main(String[] args) throws Exception {
50 | Job job = new Job();
51 |
52 | FileInputFormat.addInputPath(job, new Path(args[0]));
53 | FileOutputFormat.setOutputPath(job, new Path(args[1]));
54 | job.setMapperClass(Map.class);
55 | job.setReducerClass(Reduce.class);
56 | job.setOutputKeyClass(Text.class);
57 | job.setOutputValueClass(IntWritable.class);
58 |
59 | System.exit(job.waitForCompletion(true) ? 0 : 1);
60 | }
61 | }
--------------------------------------------------------------------------------
/java-hadoop-scalacheck/src/test/scala/MapperReducerSpecification.scala:
--------------------------------------------------------------------------------
1 | package com.company.hadoop.tests
2 |
3 | import org.scalacheck.Prop._
4 | import org.scalacheck.{Arbitrary, Gen, Properties}
5 | import java.lang.Long
6 | import org.apache.hadoop.mrunit.types.Pair
7 | import com.company.hadoop.WordCount.{Map, Reduce}
8 | import org.apache.hadoop.mrunit.mapreduce.{MapDriver, ReduceDriver}
9 | import org.apache.hadoop.io.{LongWritable, IntWritable, Text}
10 |
11 | /**
12 | * This allows us to use Int, Long and String transparently in places where we IntWritable, LongWritable
13 | * and Text are required
14 | */
15 | object HadoopImplicits {
16 | implicit def IntWritable2Int(x:IntWritable) = x.get
17 | implicit def Int2WritableInt(x:Int) = new IntWritable(x)
18 | implicit def LongWritable2Long(x:LongWritable) = x.get
19 | implicit def Long2LongWritable(x:Long) = new LongWritable(x)
20 | implicit def Text2String(x:Text) = x.toString
21 | implicit def String2Text(x:String) = new Text(x)
22 |
23 | // convert from MRUnit's Pair to a tuple for easier handling
24 | implicit def Pair2Tuple[U,T](p:Pair[U,T]):Tuple2[U,T] = (p.getFirst, p.getSecond)
25 | }
26 | import HadoopImplicits._ // required for Scala<->Hadoop type conversions
27 |
28 | /**
29 | * These are the generators and arbitrary objects that will generate random IntWritable and Text objects
30 | */
31 | object HadoopGenerators {
32 | // simple generator of IntWritable objects
33 | val intWritableGen: Gen[IntWritable] = for {
34 | num <- Gen.choose(0, 9999)
35 | } yield(new IntWritable(num))
36 |
37 | // generator of text objects (as simple words, which may be blank)
38 | val textGen: Gen[Text] = for {
39 | text <- Gen.alphaStr
40 | } yield(new Text(text))
41 |
42 | // generator of LongWritable objects, with an upper limit for the value
43 | def longWritableGen(upperRange:Int): Gen[LongWritable] = for {
44 | num <- Gen.choose(0, upperRange)
45 | } yield(new LongWritable(num))
46 |
47 | // generator of lists of IntWritables
48 | val intWritableListGen: Gen[List[IntWritable]] = Gen.listOf(intWritableGen)
49 |
50 | // generates lists of Text objects
51 | val textListGen:Gen[List[Text]] = Gen.listOf(textGen)
52 |
53 | // this is the key generator for the mapper tests: it generates a tuple where the first element is a list of random words
54 | // for the mapper, while the second element is the actual number of words in the text; the idea here is that in the
55 | // verification of the property, we only need to make sure that the mapper generated a list of words whose number of
56 | // elements is exactly the same one as calculated in the generator (which we already know is the correct length)
57 | val textLineGenWithCount: Gen[(Text,Int)] = for {
58 | words <- textListGen // generates a line of strings
59 | } yield((new Text(words.mkString(" ")), words.filter(_.toString.trim != "").size))
60 | // the second part of the yield() clause above is neede because textGen may generate empty words, which are ignored
61 | // the mapper, so we need to make sure that when counting the correct number of words, we ignore the empty ones
62 |
63 | // Generates tuples of lists of IntWritable objects as well as their pre-calculated total. This generator is the key
64 | // generator for the reducer property checks. Please note that it may generate tuples with empty lists, to which the reducer
65 | // will (should) respond by generating no output
66 | val intWritableListWithSum: Gen[(List[IntWritable], Int)] = for {
67 | l <- intWritableListGen
68 | } yield((l, l.foldLeft(0)((x,total) => x + total)))
69 | }
70 |
71 | object WordCountSpecification extends Properties("Mapper and reducer tests") {
72 |
73 | import scala.collection.JavaConversions._ // import our generators and implicit conversions
74 | import HadoopGenerators._ // import our arbitrary generators into the scope
75 |
76 | // create mapper and reducers
77 | val reducer = new Reduce
78 | val mapper = new Map
79 |
80 | // this is used later in some of the comparisons
81 | case object one extends IntWritable(1)
82 |
83 | property("The mapper correctly maps single words") = forAll(longWritableGen(99999), textGen) {(key:LongWritable, value:Text) =>
84 | val driver = new MapDriver(mapper)
85 |
86 | // we only need to verify that for input strings containing a single word, the mapper always returns that single word
87 | val results = driver.withInput(key, value).run
88 |
89 | // The result of the processing will be true if results.headOption returned a None, because we need to account
90 | // for empty lines (which would not generate an key,value output from the mapper)
91 | results.headOption.map(pair => pair._1 == value && pair._2 == one).getOrElse(true)
92 | }
93 |
94 | property("The mapper correctly maps lines with multiple words") =
95 | forAll(longWritableGen(99999), textLineGenWithCount) { case (key, (valueList, valueTotal)) =>
96 | val driver = new MapDriver(mapper)
97 | val results = driver.withInput(key, valueList).run
98 |
99 | (results.forall(one == _._2) && // checks that for all pairs of (key,value), the value is "1" and...
100 | // here _ is a shorthand to the pairs generated by the mapper
101 | results.size == valueTotal) // ensure that then total amount of pairs from the mapper is as expected
102 | }
103 |
104 |
105 | property("The reducer correctly aggregates data") = forAll(textGen, intWritableListWithSum) { case(key, (valueList, valueTotal)) => {
106 | // mrunit driver - it's more convenient to use mrunit as it will automatically set up our reduce contexts, which
107 | // otherwise are not so easy to create directly
108 | val driver = new ReduceDriver(reducer)
109 |
110 | // set up the input and expected output values based on ScalaTest's random data
111 | val results = driver.withInput(key, valueList).run
112 |
113 | // The reducer generates at most one key,value pair for each input key,value pair, but it may generate
114 | // nothing for empty keys so we need to be careful with that. List.headOption will return None if the list is empty,
115 | // and in that case map() won't be applied (can only be used with non-empty lists) and it will evaluate to true
116 | // straight away. In the list had a (key,value) pair in it, we can extract the second element from the tuple
117 | // with _2 and then "unbox" its Int value (otherwise it won't work)
118 | results.headOption.map(_._2.get == valueTotal).getOrElse(true) == true
119 | }
120 | }
121 | }
122 |
123 | object TestRunner extends App {
124 | WordCountSpecification.check
125 | }
--------------------------------------------------------------------------------
/java-scalacheck/README.txt:
--------------------------------------------------------------------------------
1 | This example shows how to use ScalaCheck's random test data generator features to create
2 | a custom data generator.
3 |
4 | Only the ScalaCheck 1.9 library is required.
5 |
6 | Execute the tests:
7 |
8 | mvn scala:run -Dlauncher=test
--------------------------------------------------------------------------------
/java-scalacheck/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.company.scala
5 | com.company.scala
6 | 1.0-SNAPSHOT
7 | 2008
8 |
9 | 2.9.0-1
10 |
11 |
12 |
13 |
14 | scala-tools.org
15 | Scala-Tools Maven2 Repository
16 | http://scala-tools.org/repo-releases
17 |
18 |
19 | maven.org
20 | Maven2 repository
21 | http://repo1.maven.org/maven2
22 |
23 |
24 |
25 |
26 |
27 | scala-tools.org
28 | Scala-Tools Maven2 Repository
29 | http://scala-tools.org/repo-releases
30 |
31 |
32 |
33 |
34 |
35 | org.scala-lang
36 | scala-library
37 | ${scala.version}
38 |
39 |
40 | junit
41 | junit
42 | 4.4
43 | test
44 |
45 |
46 | commons-lang
47 | commons-lang
48 | 2.5
49 |
50 |
51 | org.scala-tools.testing
52 | scalacheck_2.9.0-1
53 | 1.9
54 |
55 |
56 | org.scala-tools.testing
57 | specs_2.9.0-1
58 | 1.6.8
59 |
60 |
61 | org.scala-lang
62 | jline
63 | 2.9.0-1
64 |
65 |
66 |
67 | src/main/
68 | src/test/
69 |
70 |
71 | org.scala-tools
72 | maven-scala-plugin
73 |
74 |
75 |
76 | compile
77 | testCompile
78 |
79 |
80 |
81 |
82 | ${scala.version}
83 |
84 | -target:jvm-1.5
85 |
86 |
87 |
88 |
89 | test
90 | com.company.test.Runner
91 |
92 |
93 |
94 |
95 |
96 | org.apache.maven.plugins
97 | maven-eclipse-plugin
98 |
99 | true
100 |
101 | ch.epfl.lamp.sdt.core.scalabuilder
102 |
103 |
104 | ch.epfl.lamp.sdt.core.scalanature
105 |
106 |
107 | org.eclipse.jdt.launching.JRE_CONTAINER
108 | ch.epfl.lamp.sdt.launching.SCALA_CONTAINER
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | org.scala-tools
118 | maven-scala-plugin
119 |
120 | ${scala.version}
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/java-scalacheck/src/main/com/company/account/Account.java:
--------------------------------------------------------------------------------
1 | package com.company.account;
2 |
3 | /**
4 | * Implementation of a simple bank account class
5 | */
6 | public class Account {
7 |
8 | public final static int GOLD_AGE = 50;
9 | public final static double GOLD_BALANCE = 10000;
10 | public final static double STD_INTEREST = .02;
11 | public final static double GOLD_INTEREST = .03;
12 |
13 | private int id;
14 | private int age;
15 | private double balance;
16 |
17 | public Account(int id, int age, double balance) {
18 | this.id = id;
19 | this.age = age;
20 | this.balance = balance;
21 | }
22 |
23 | public double getBalance() {
24 | return(balance);
25 | }
26 |
27 | public int getAge() {
28 | return(age);
29 | }
30 |
31 | public void deposit(double amt) {
32 | assert (amt > 0);
33 | balance += amt;
34 | }
35 |
36 | public void withdraw(double amt) throws InsufficientFundsException {
37 | assert(amt > 0);
38 | if (amt <= this.balance)
39 | balance -= amt;
40 | else
41 | throw new InsufficientFundsException();
42 | }
43 |
44 | public double getRate() {
45 | if (balance < Account.GOLD_BALANCE && age < Account.GOLD_AGE)
46 | return(Account.STD_INTEREST);
47 | else
48 | return(Account.GOLD_INTEREST);
49 | }
50 |
51 | public void creditInterest() {
52 | deposit(getRate() * balance);
53 | }
54 | }
--------------------------------------------------------------------------------
/java-scalacheck/src/main/com/company/account/InsufficientFundsException.java:
--------------------------------------------------------------------------------
1 | package com.company.account;
2 |
3 | /**
4 | * Implementation of the InsufficientFundsException Scala class in Java
5 | */
6 | public class InsufficientFundsException extends Exception {
7 | }
--------------------------------------------------------------------------------
/java-scalacheck/src/test/com/company/test/AccountSpecification.scala:
--------------------------------------------------------------------------------
1 | package com.company.test
2 |
3 | import com.company.account.Account
4 | import com.company.account.InsufficientFundsException
5 | import org.scalacheck.Prop._
6 | import org.scalacheck.{Prop, Arbitrary, Gen, Properties}
7 | import com.company.account.Account
8 | import scala.Some
9 |
10 | /**
11 | * Custom data generator for the Account class
12 | */
13 | object GenAccount {
14 |
15 | import com.company.account.Account._
16 |
17 | val MAX_ID: Int = 999999
18 | val MAX_AGE: Int = 200
19 | val MAX_BALANCE: Double = 10 * GOLD_BALANCE
20 |
21 | /**
22 | * This method takes care of generating new objects of the Account class, using
23 | * random values defined within a certain range
24 | */
25 | def genAccount(maxId: Int, maxAge: Int, maxBalance: Double): Gen[Account] = for {
26 | id <- Gen.choose(0, maxId)
27 | age <- Gen.choose(0, maxAge)
28 | balance <- Gen.choose(0, maxBalance)
29 | } yield new Account(id, age, balance)
30 |
31 | /**
32 | * The Arbitrary generator creates test data of any type.
33 | * This must be defined as an implicit value or function and imported into the scope of the
34 | * ScalaCheck tests so that ScalaCheck can generate test data of the required type
35 | */
36 | implicit val arbAccount: Arbitrary[Account] =
37 | Arbitrary(genAccount(MAX_ID, MAX_AGE, MAX_BALANCE))
38 | }
39 |
40 |
41 | object AccountSpecification extends Properties("Account") {
42 |
43 | import com.company.account.Account._
44 | import GenAccount._
45 |
46 | val genAcctAmt: Gen[(Account, Double)] = for {
47 | acct <- Arbitrary.arbitrary[Account]
48 | amt <- Gen.choose(0.01, MAX_BALANCE)
49 | } yield (acct, amt)
50 |
51 | // In this example this arbitrary object is not used, but this is how it would look like, if required:
52 | implicit val arbAccountAmount: Arbitrary[(Account, Double)] =
53 | Arbitrary(genAcctAmt)
54 |
55 | property("Deposit") = forAll(genAcctAmt) {
56 | case (acct: Account, amt: Double) =>
57 | val oldBalance = acct.getBalance()
58 | acct.deposit(amt)
59 | acct.getBalance() == oldBalance + amt
60 | }
61 |
62 | /**
63 | * This is the same property checks as above, but uses the arbitrary generator of
64 | * tuples of type (Account, Double) instead of providing a reference to the generator
65 | * function. PLease note how the code is a little longer due to the extra type-related boilerplate
66 | */
67 | property("Deposit-with-Arbitrary") = forAll { (input:(Account,Double)) =>
68 | input match {
69 | case (acct: Account, amt: Double) =>
70 | val oldBalance = acct.getBalance()
71 | acct.deposit(amt)
72 | acct.getBalance() == oldBalance + amt
73 | }
74 | }
75 |
76 | property("Withdraw-normal") = forAll(genAcctAmt) {
77 | case (acct: Account, amt: Double) =>
78 | amt <= acct.getBalance() ==> {
79 | val oldBalance = acct.getBalance()
80 | acct.withdraw(amt)
81 | acct.getBalance() == oldBalance - amt
82 | }
83 | }
84 |
85 | //
86 | // This test can be done more elegantly in combination with ScalaTest
87 | //
88 | property("Withdraw-overdraft") = forAll(genAcctAmt) {
89 | case (acct: Account, amt: Double) =>
90 | amt > acct.getBalance() ==> {
91 | val oldBalance = acct.getBalance()
92 | Prop.throws(acct.withdraw(amt), classOf[InsufficientFundsException]) && acct.getBalance() == oldBalance
93 | }
94 | }
95 |
96 | property("Rate-lowBalance, lowAge") = {
97 | val gen = genAccount(MAX_ID, GOLD_AGE - 1, GOLD_BALANCE - .01)
98 |
99 | forAll(gen) {
100 | acct: Account => acct.getRate() == STD_INTEREST
101 | }
102 | }
103 |
104 | property("Rate-highBalance") = forAll { (acct: Account) =>
105 | acct.getBalance() >= GOLD_BALANCE ==> (acct.getRate() == GOLD_INTEREST)
106 | }
107 |
108 | property("Rate-highAge") = forAll { acct: Account =>
109 | acct.getAge() >= GOLD_AGE ==> (acct.getRate() == GOLD_INTEREST)
110 | }
111 |
112 | property("CreditInterest") = forAll {
113 | acct: Account =>
114 | val oldBalance = acct.getBalance()
115 | acct.creditInterest()
116 | acct.getBalance() == oldBalance + (oldBalance * acct.getRate())
117 | }
118 | }
119 |
120 | /**
121 | * Used for running the tests from the command line, as there are no JUnit
122 | * compatible runners for ScalaCheck (could be done if ScalaCheck is used
123 | * from ScalaTest
124 | */
125 | object Runner {
126 | val rnd = new java.util.Random(100)
127 | //val parms = org.scalacheck.Test.Params(75,500,0,20,rnd,1,20)
128 | val parms = org.scalacheck.Test.Params(75, 500, 0, 20, rnd, 1)
129 |
130 | def apply() = {
131 | AccountSpecification.check(parms)
132 | }
133 |
134 | def main(args: Array[String]) = apply()
135 | }
--------------------------------------------------------------------------------
/java-scalacheck/src/test/com/company/test/AccountTest.java:
--------------------------------------------------------------------------------
1 | package com.company.test;
2 |
3 | import org.junit.Test;
4 | import static org.junit.Assert.*;
5 | import static org.junit.Assert.assertEquals;
6 | import com.company.account.*;
7 |
8 | /**
9 | * This code is the equivalent of AccountSpecification but written for JUnit 4
10 | * and without the automatic generation of test data
11 | */
12 | public class AccountTest {
13 |
14 | private static double DEFAULT_DELTA = 0;
15 |
16 | @Test
17 | public void testDeposit() {
18 | // simple deposit
19 | Account acct = new Account(1, 1, 10); // id, age, balance
20 | double oldBalance = acct.getBalance();
21 | double amt = 100;
22 | acct.deposit(amt);
23 | assertEquals(acct.getBalance(), oldBalance + amt, DEFAULT_DELTA);
24 | }
25 |
26 | @Test
27 | public void testDepositWithNegativeBalance() {
28 | // deposit when we start with a negative balance
29 | Account acct = new Account(1, 1, -100); // id, age, balance
30 | double amt = 100;
31 | acct.deposit(amt);
32 | assertEquals(acct.getBalance(), 0, DEFAULT_DELTA);
33 | }
34 |
35 | @Test
36 | public void withdrawNormal() throws InsufficientFundsException {
37 | Account acct = new Account(1, 1, 100);
38 | double oldBalance = acct.getBalance();
39 | double amt = 50;
40 | acct.withdraw(amt);
41 | assertEquals(acct.getBalance(), oldBalance - amt, DEFAULT_DELTA);
42 | }
43 |
44 | @Test
45 | public void withdrawOverdraft() {
46 | Account acct = new Account(1, 1, 100);
47 | double amt = 50;
48 |
49 | boolean exception = true;
50 | try {
51 | acct.withdraw(amt);
52 | } catch(Exception ex) {
53 | exception = ex instanceof InsufficientFundsException;
54 | }
55 | assertTrue("InsufficientFundsException should have been thrown", exception);
56 | }
57 |
58 | @Test
59 | public void Rate_lowBalance_lowAge() {
60 | Account acct = new Account(1, Account.GOLD_AGE - 1, Account.GOLD_BALANCE - 0.01);
61 | assertEquals(Account.STD_INTEREST, acct.getRate(), 0);
62 | }
63 |
64 | @Test
65 | public void rateHighBalance() {
66 | Account acct = new Account(1, 1, Account.GOLD_BALANCE);
67 | assertEquals(Account.GOLD_INTEREST, acct.getRate(), DEFAULT_DELTA);
68 | }
69 |
70 | @Test
71 | public void rateHighAge() {
72 | Account acct = new Account(1, Account.GOLD_AGE, 100);
73 | assertEquals(Account.GOLD_INTEREST, acct.getRate(), DEFAULT_DELTA);
74 | }
75 |
76 | @Test
77 | public void creditInterest() {
78 | Account acct = new Account(1,1,100);
79 | double oldBalance = acct.getBalance();
80 | acct.creditInterest();
81 | assertEquals(acct.getBalance(), oldBalance + ( oldBalance * acct.getRate()), DEFAULT_DELTA);
82 | }
83 | }
--------------------------------------------------------------------------------
/scalacheck-basic/README.txt:
--------------------------------------------------------------------------------
1 | This example shows how to use ScalaCheck's random test data generator features to create
2 | a custom data generator.
3 |
4 | Only the ScalaCheck 1.9 library is required.
5 |
6 | Execute the tests:
7 |
8 | mvn scala:run -Dlauncher=test
--------------------------------------------------------------------------------
/scalacheck-basic/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.company.scala
5 | scalacheck-simple
6 | 1.0-SNAPSHOT
7 | 2008
8 |
9 | 2.9.0-1
10 |
11 |
12 |
13 |
14 | scala-tools.org
15 | Scala-Tools Maven2 Repository
16 | http://scala-tools.org/repo-releases
17 |
18 |
19 | scala-tools-snapshots.org
20 | Scala-Tools Maven2 Snapshot Repository
21 | http://scala-tools.org/repo-snapshots
22 |
23 |
24 | maven.org
25 | Maven2 repository
26 | http://repo1.maven.org/maven2
27 |
28 |
29 |
30 |
31 |
32 | scala-tools.org
33 | Scala-Tools Maven2 Repository
34 | http://scala-tools.org/repo-releases
35 |
36 |
37 |
38 |
39 |
40 | org.scala-lang
41 | scala-library
42 | ${scala.version}
43 |
44 |
45 | junit
46 | junit
47 | 4.4
48 | test
49 |
50 |
51 | commons-lang
52 | commons-lang
53 | 2.5
54 |
55 |
56 | org.scala-tools.testing
57 | scalacheck_2.9.0-1
58 | 1.9
59 |
60 |
61 | org.scala-tools.testing
62 | specs_2.9.0-1
63 | 1.6.8
64 |
65 |
66 | org.scala-lang
67 | jline
68 | 2.9.0-1
69 |
70 |
71 |
72 | src/main/
73 | src/test/
74 |
75 |
76 | org.scala-tools
77 | maven-scala-plugin
78 |
79 |
80 |
81 | compile
82 | testCompile
83 |
84 |
85 |
86 |
87 | ${scala.version}
88 |
89 | -target:jvm-1.5
90 |
91 |
92 |
93 |
94 | test
95 | com.company.scalacheck.Runner
96 |
97 |
98 |
99 |
100 |
101 | org.apache.maven.plugins
102 | maven-eclipse-plugin
103 |
104 | true
105 |
106 | ch.epfl.lamp.sdt.core.scalabuilder
107 |
108 |
109 | ch.epfl.lamp.sdt.core.scalanature
110 |
111 |
112 | org.eclipse.jdt.launching.JRE_CONTAINER
113 | ch.epfl.lamp.sdt.launching.SCALA_CONTAINER
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | org.scala-tools
123 | maven-scala-plugin
124 |
125 | ${scala.version}
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/scalacheck-basic/src/main/scala/Rectangle.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | /**
4 | * Simple case class that will be used as the basis for our examples
5 | */
6 | case class Rectangle(val width:Double, val height:Double) {
7 | // when the width is a multiple of 3, this will fail
8 | lazy val area = if(width % 11 ==0) (width * 1.0001 * height) else (width * height)
9 | // valid version of the method above
10 | lazy val areaCorrect = (width * height)
11 | lazy val perimeter = (2*width) + (2*height)
12 | def biggerThan(r:Rectangle) = (area > r.area)
13 | }
--------------------------------------------------------------------------------
/scalacheck-basic/src/test/scala/DataCollection.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalacheck.Properties
4 | import org.scalacheck.Prop._
5 |
6 | object DataCollectionSpecification extends Properties("Data collection examples") {
7 | import RectangleGenerator._
8 |
9 | /**
10 | * The Prop.collect method is the easiest way to group our test data, but depending on the classification
11 | * criteria for our data, it is possible that data cannot be grouped in a meaningful way.
12 | * In the following example, the console output will show a long list of pairs of
13 | * height and width, each one of them with a 1% frequency
14 | */
15 | property("data collection spec with Prop.collect") = forAll { (r:Rectangle) =>
16 | collect((r.width, r.height)) {
17 | r.areaCorrect == (r.width * r.height)
18 | }
19 | }
20 |
21 | /**
22 | * Helper function to help us classify input data. Using a partfial function we can plug in some pattern
23 | * matching with guards that define how our input data gets classified. The return value of the function
24 | * is a string (this function always returns something) that is used by ScalaCheck as the grouping criteria
25 | * for our data
26 | */
27 | val collector: PartialFunction[Rectangle,String] = {
28 | case r if r.perimeter < 10000 => "small"
29 | case r if r.perimeter > 10000 && r.perimeter < 25000 => "medium"
30 | case r if r.perimeter > 25000 => "large"
31 | }
32 |
33 | /**
34 | * This is the same check as the previous example, but now data is grouped using our collector
35 | * custom function. The console output now is suddenly more meaningful for analysis
36 | */
37 | property("data collection spec with Prop.collect and a grouping function") = forAll { (r:Rectangle) =>
38 | collect(collector(r)) {
39 | r.areaCorrect == (r.width * r.height)
40 | }
41 | }
42 |
43 | /**
44 | * Here we use the "binary" version of Prop.classify to classify our random rectangle objects into "taller" or "wider",
45 | * and then with Prop.collect we've collected the data using our previous method. The output now is a two-level
46 | * grouping of our data.
47 | * The example also shows how Prop.classify and Prop.collect can be combined within the same property check,
48 | * and even multiple calls can be nested to obtain a more granular classification
49 | */
50 | property("data collection spec with Prop.classify and Prop.collect") = forAll { (r:Rectangle) =>
51 | classify(r.height > r.width, "taller", "wider") {
52 | collect(collector(r)) {
53 | r.areaCorrect == (r.width * r.height)
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/scalacheck-basic/src/test/scala/Generators.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalacheck.Prop._
4 | import org.scalacheck.{Arbitrary, Properties, Gen}
5 |
6 | object SimpleGenerator extends Properties("Simple sample generator") {
7 | // simple generator based on the example given in the ScalaCheck documentation
8 | // TODO: create a more suitable one?
9 | val myGen = for {
10 | n <- Gen.choose(10,20)
11 | m <- Gen.choose(2*n, 500)
12 | } yield (n,m)
13 |
14 | // simple function
15 | def myFunction(n1:Int, n2:Int) = n1 + n2
16 |
17 | property("Test property with generator") = forAll(myGen) {(n:(Int,Int)) =>
18 | n match {
19 | case (n1, n2) => (n1 + n2) == myFunction(n1,n2)
20 | }
21 | }
22 | }
23 |
24 | /**
25 | * Specification with a Generator that is used to create case classes and verify the
26 | * data in them.
27 | *
28 | * In this example the generator returns both the case class with specific values as well as
29 | * the specific values that were used to genreate the case class, so that its calculations
30 | * can be verified
31 | */
32 | object RectangleSpecification extends Properties("Rectangle specification") {
33 |
34 | val rectangleGen:Gen[(Rectangle, Double,Double)] = for {
35 | height <- Gen.choose(0,9999)
36 | width <- Gen.choose(0,9999)
37 | } yield((Rectangle(width, height), width,height))
38 |
39 | property("Test area") = forAll(rectangleGen) { (input:(Rectangle,Double,Double)) => input match {
40 | case(r, width, height) => r.area == width * height
41 | }}
42 | }
43 |
44 | /**
45 | * Generator of case objects for the Rectangle class, as well as an arbitrary generator
46 | */
47 | object RectangleGenerator {
48 | // generator for the Rectangle case class
49 | val rectangleGen:Gen[Rectangle] = for {
50 | height <- Gen.choose(0,9999)
51 | width <- Gen.choose(0,9999)
52 | } yield(Rectangle(width, height))
53 |
54 | // Arbitrary generator of rectangles
55 | implicit val arbRectangle: Arbitrary[Rectangle] = Arbitrary(rectangleGen)
56 | }
57 |
58 | /**
59 | * This property shows the advantage of using an arbitrary generator, as ScalaCheck will then
60 | * be able to automatically generate the test data using the implicit arbitrary generator in scope,
61 | * and we don't need to provide a generator object as a parameter to the forAll method
62 | *
63 | * In this case, the arbitrary generator is in the scope after the import statement to import
64 | * the arbitrary function
65 | */
66 | object ArbitraryRectangleSpecification extends Properties("Rectangle specification with an Arbitrary generator") {
67 | import RectangleGenerator._
68 |
69 | // generate two random rectangles and check which one is bigger
70 | property("Test biggerThan") = forAll{ (r1:Rectangle, r2:Rectangle) =>
71 | (r1 biggerThan r2) == (r1.area > r2.area)
72 | }
73 |
74 | // please note that if not using an arbitrary generator, then we need to provide a generator parameter
75 | // for each one of the random data parameters. Therefore, the following code is equivalent to the
76 | // one above:
77 | property("Test biggerThan") = forAll(rectangleGen, rectangleGen){ (r1:Rectangle, r2:Rectangle) =>
78 | (r1 biggerThan r2) == (r1.area > r2.area)
79 | }
80 | }
--------------------------------------------------------------------------------
/scalacheck-basic/src/test/scala/Runner.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | object Runner {
4 | def main(args: Array[String]) = {
5 | // run the properties
6 | SimpleProperties.run
7 | BiggerSpecification.check // holds
8 | SecondSpecification.check // holds
9 | GroupedSpecifications.check // holds
10 | SimpleGenerator.check // holds?
11 | RectangleSpecification.check // holds?
12 | ArbitraryRectangleSpecification.check // holds?
13 | DataCollectionSpecification.check // shows random data collection
14 | }
15 | }
--------------------------------------------------------------------------------
/scalacheck-basic/src/test/scala/SimpleSpecification.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalacheck.Prop._
4 | import org.scalacheck.Properties
5 | import java.lang.Math
6 |
7 | /**
8 | * Grouping of properties into a single specification object
9 | */
10 | object BiggerSpecification extends Properties("A bigger test specification") {
11 | property("testList") = forAll { (l1: List[Int], l2: List[Int]) =>
12 | l1.size + l2.size == (l1 ::: l2).size
13 | }
14 |
15 | property("Check concatenated string") = forAll { (a:String, b:String) =>
16 | a.concat(b) == a + b
17 | }
18 | }
19 |
20 | object SecondSpecification extends Properties("A second specification") {
21 | // TODO: not a very original example, just like the other ones...
22 | property("Check string length") = forAll { n: Int =>
23 | (n >= 0 && n < 10000) ==> ((List.fill(n)("").length == n))
24 | }
25 | }
26 |
27 | object GroupedSpecifications extends Properties("AllApplicationSpecifications") {
28 | include(BiggerSpecification)
29 | include(SecondSpecification)
30 | }
31 |
32 | /**
33 | * Used for running the tests from the command line, as there are no JUnit
34 | * compatible runners for ScalaCheck (could be done if ScalaCheck is used
35 | * from ScalaTest
36 | */
37 | object SimpleProperties {
38 |
39 | def run = {
40 |
41 | // a very simple property
42 | val workingProperty = forAll { (l1: List[Int], l2: List[Int]) =>
43 | l1.size + l2.size == (l1 ::: l2).size
44 | }
45 |
46 | // this one fails for negative numbers
47 | val failingProperty = forAll {(n:Double) =>
48 | Math.sqrt((n*2)) == n
49 | }
50 |
51 | // this is the correct version of the property aobve
52 | val validSqrtProperty = forAll {(n:Double) =>
53 | (n > 0) ==> (Math.sqrt((n*2)) == n)
54 | }
55 |
56 | // property combination
57 | val combinedProperty1 = workingProperty && failingProperty
58 | val combinedProperty2 = atLeastOne(workingProperty, failingProperty)
59 |
60 | // run the properties and grouped specifications
61 | workingProperty.check // holds
62 | failingProperty.check // fails
63 | validSqrtProperty.check // holds
64 | combinedProperty1.check // fails
65 | combinedProperty2.check // holds
66 | }
67 | }
--------------------------------------------------------------------------------
/scalacheck-integration-junit/README.txt:
--------------------------------------------------------------------------------
1 | A few basic examples of integrating ScalaCheck with plain JUnit test cases.
2 |
3 | ScalaCheck 1.9 as well as JUnit 4.4 are required.
4 |
5 | This code requires Maven to build and run the test cases. The test cases can be run with the "mvn test" command.
6 |
--------------------------------------------------------------------------------
/scalacheck-integration-junit/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.company.scala
5 | scalacheck-integration-junit
6 | 1.0-SNAPSHOT
7 | 2008
8 |
9 | 2.9.0-1
10 |
11 |
12 |
13 |
14 | scala-tools.org
15 | Scala-Tools Maven2 Repository
16 | http://scala-tools.org/repo-releases
17 |
18 |
19 | scala-tools-snapshots.org
20 | Scala-Tools Maven2 Snapshot Repository
21 | http://scala-tools.org/repo-snapshots
22 |
23 |
24 | maven.org
25 | Maven2 repository
26 | http://repo1.maven.org/maven2
27 |
28 |
29 |
30 |
31 |
32 | scala-tools.org
33 | Scala-Tools Maven2 Repository
34 | http://scala-tools.org/repo-releases
35 |
36 |
37 |
38 |
39 |
40 | org.scala-lang
41 | scala-library
42 | ${scala.version}
43 |
44 |
45 | junit
46 | junit
47 | 4.4
48 | test
49 |
50 |
51 | commons-lang
52 | commons-lang
53 | 2.5
54 |
55 |
56 | org.scala-tools.testing
57 | scalacheck_2.9.0-1
58 | 1.9
59 |
60 |
61 | org.scala-lang
62 | jline
63 | 2.9.0-1
64 |
65 |
66 |
67 | src/main/
68 | src/test/
69 |
70 |
71 | org.scala-tools
72 | maven-scala-plugin
73 |
74 |
75 |
76 | compile
77 | testCompile
78 |
79 |
80 |
81 |
82 | ${scala.version}
83 |
84 | -target:jvm-1.5
85 |
86 |
87 |
88 |
89 | test
90 | com.company.scalacheck.Runner
91 |
92 |
93 |
94 |
95 |
96 | org.apache.maven.plugins
97 | maven-eclipse-plugin
98 |
99 | true
100 |
101 | ch.epfl.lamp.sdt.core.scalabuilder
102 |
103 |
104 | ch.epfl.lamp.sdt.core.scalanature
105 |
106 |
107 | org.eclipse.jdt.launching.JRE_CONTAINER
108 | ch.epfl.lamp.sdt.launching.SCALA_CONTAINER
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | org.scala-tools
118 | maven-scala-plugin
119 |
120 | ${scala.version}
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/scalacheck-integration-junit/src/main/scala/Rectangle.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | /**
4 | * Simple case class that will be used as the basis for our examples
5 | */
6 | case class Rectangle(val width:Double, val height:Double) {
7 | // when the width is a multiple of 11, this will fail
8 | lazy val area = if(width % 3 ==0) (width * 1.0001 * height) else (width * height)
9 | // valid version of the method above
10 | lazy val areaCorrect = (width * height)
11 | lazy val perimeter = (2*width) + (2*height)
12 | def biggerThan(r:Rectangle) = (area > r.area)
13 | }
--------------------------------------------------------------------------------
/scalacheck-integration-junit/src/test/scala/BasicScalaJUnitTest.scala:
--------------------------------------------------------------------------------
1 | package com.company.junit
2 |
3 | import org.junit.Assert._
4 | import org.junit.Test
5 | import com.company.scalacheck.Rectangle
6 | import org.scalacheck.Test.Params
7 | import org.scalacheck.{ConsoleReporter, Prop, Properties, Arbitrary, Gen, Test => SchkTest}
8 | import org.scalacheck.Prop._
9 |
10 | /**
11 | * Generator of case objects for the Rectangle class, as well as an arbitrary generator
12 | */
13 | object RectangleGenerator {
14 | // generator for the Rectangle case class
15 | val rectangleGen: Gen[Rectangle] = for {
16 | height <- Gen.choose(0, 9999)
17 | width <- Gen.choose(0, 9999)
18 | } yield (Rectangle(width, height))
19 |
20 | // Arbitrary generator of rectangles
21 | implicit val arbRectangle: Arbitrary[Rectangle] = Arbitrary(rectangleGen)
22 | }
23 |
24 | class ScalaJUnitSimpleTest {
25 | val validProperty = Prop.forAll { (a:String) =>
26 | (a.length > 0) ==> (a + a == a.concat(a))
27 | }
28 |
29 | @Test def testSquareRoot = {
30 | assertTrue(SchkTest.check(Params(testCallback = ConsoleReporter()), validProperty).passed)
31 | }
32 | }
33 |
34 | /**
35 | * This is an example of a JUnit test case implemented in Scala, where the actual testing logic is implemented
36 | * as ScalaCheck property checks that are evaluated with JUnit's assertTrue assertion. Each one of the test
37 | * cases is implemented with a different approach, up until the last one that uses an implicit function to allow
38 | * to provide Prop objects (as generated by Prop.forAll) to JUnit's assertTrue
39 | *
40 | * The ScalaCheck property checks are regular properties that use an arbitrary generator.
41 | *
42 | * The only kind of integration visible here is through the doCheck method, that is able to execute a property check
43 | * and return true or false depending on the result reported by ScalaCheck; the returned value is then suitable to
44 | * be provided to assertTrue to report success or failure to JUnit
45 | */
46 | class ScalaJUnitTest {
47 |
48 | import RectangleGenerator._
49 |
50 | @Test def simpleTest = {
51 | val r = Rectangle(4, 5)
52 | assertTrue("Area does not match", r.areaCorrect == (4 * 5))
53 | }
54 |
55 | /**
56 | * Two properties for our testing purposes. The first one fails, while
57 | * the second one holds true
58 | */
59 | val failedTest = Prop.forAll { (r: Rectangle) =>
60 | r.area == (r.height * r.width)
61 | }
62 | val validTest = Prop.forAll { (r: Rectangle) =>
63 | r.areaCorrect == (r.height * r.width)
64 | }
65 |
66 | /**
67 | * This property should not hold, and it should be reported as a JUnit assertion failure
68 | */
69 | @Test def testWithFailedPropertyCheck = {
70 | assertTrue(doCheck(failedTest))
71 | }
72 |
73 | /**
74 | * This property check holds true, and it should show as a successful test execution in JUnit
75 | */
76 | @Test def testWithValidPropertyCheck = {
77 | assertTrue(doCheck(validTest))
78 | }
79 |
80 | /**
81 | * With this implicit conversion we can pass a Prop object (such as the result of a Prop.forAll call) to
82 | * JUnit's assertTrue
83 | */
84 | implicit def doCheck(p: Prop): Boolean = SchkTest.check(Params(testCallback = ConsoleReporter()), p).passed
85 |
86 | @Test def testWithImplicitConversion = {
87 | assertTrue(validTest)
88 | }
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/scalacheck-integration-junit/src/test/scala/ScalaCheckJUnit.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck.support
2 |
3 | import org.junit.Test
4 | import org.junit.Assert._
5 | import org.junit.runner.Description
6 | import org.scalacheck._
7 | import org.scalacheck.{Test=>SchkTest}
8 | import org.scalacheck.Test.Params._
9 | import org.scalacheck.Test.Params
10 | import scala.Some
11 | import org.junit.runner.notification.{Failure, RunNotifier}
12 | import java.lang.{Throwable, Boolean}
13 |
14 | /**
15 | * can be mixed into any class to provide a doCheck method that can be used to run a ScalaCheck property. The
16 | * method returns True or False depending on whether the given property holds true or not
17 | */
18 | trait ScalaCheckJUnitSupport {
19 | // by default this is a verbose console reporter, so that we can see ScalaCheck's output
20 | // in the console
21 | private class CustomConsoleReporter extends ConsoleReporter(1)
22 | implicit def doCheck(p: Properties): Boolean = SchkTest.check(Params(testCallback = new CustomConsoleReporter), p).passed
23 | }
24 |
25 | /**
26 | * This trait can only be mixed into classes implementing ScalaCheck's Properties class, and takes care
27 | * of automatically running all properties in the class as defined via Properties.property()
28 | */
29 | trait ScalaCheckJUnitAdapter extends ScalaCheckJUnitSupport {
30 | self: Properties =>
31 |
32 | @Test def runAllProperties = {
33 | System.out.println("==== \nRunning property " + name + ": ")
34 | assertTrue("Property did not hold true", doCheck(this))
35 | }
36 | }
37 |
38 | /**
39 | * This a JUnit runner that allows to run ScalaCheck properties (created into an object that implements
40 | * Properties) as part of a JUnit test suite. Each property will be counted as a failure or passed test
41 | * by JUnit.
42 | *
43 | * Properties are written in the exact same way as pure ScalaCheck; the only aifference is that the test suite class
44 | * needs to be annotated with @RunWith[classOf[ScalaCheckJUnitPropertiesRunner]] so that JUnit knows how to run
45 | * the tests
46 | */
47 | class ScalaCheckJUnitPropertiesRunner(suiteClass: java.lang.Class[Properties]) extends org.junit.runner.Runner {
48 |
49 | private val properties = suiteClass.newInstance
50 |
51 | lazy val getDescription = createDescription(properties)
52 |
53 | /**
54 | * Create a description
55 | */
56 | private def createDescription(props: Properties): Description = {
57 | val description = Description.createSuiteDescription(props.name)
58 | props.properties.foreach(p => Description.createTestDescription(p._2.getClass, p._1))
59 | description
60 | }
61 |
62 | // Our custom tes callback, used to keep JUnit's runner updated about test progress
63 | private class CustomTestCallback(notifier:RunNotifier, desc: Description) extends SchkTest.TestCallback {
64 | // TODO: is it even possible to obtain the correct stack trace? ScalaCheck doesn't throw Exceptions for property failures!
65 | def failure = new Failure(desc, new Throwable("ScalaCheck property did not hold true"))
66 |
67 | /** Called whenever a property has finished testing */
68 | override def onTestResult(name: String, res: SchkTest.Result) = {
69 | res.status match {
70 | case SchkTest.Passed => {} // Test passed, nothing to do
71 | case SchkTest.Proved(_) => {} // Test passed, nothing to do
72 | case SchkTest.Exhausted => notifier.fireTestIgnored(desc) // exhausted tests are marked as ignored in JUnit
73 | case _ => notifier.fireTestFailure(failure) // everything else is a failed test
74 | }
75 | }
76 | }
77 |
78 | // we'll use this one to report status to the console, and we'll chain it with our custom reporter
79 | val consoleReporter = new ConsoleReporter(1)
80 |
81 | /**
82 | * Run this Suite
of tests, reporting results to the passed RunNotifier
.
83 | * This class's implementation of this method invokes run
on an instance of the
84 | * suiteClass
Class
passed to the primary constructor, passing
85 | * in a Reporter
that forwards to the RunNotifier
passed to this
86 | * method as notifier
.
87 | *
88 | * @param notifier the JUnit RunNotifier
to which to report the results of executing
89 | * this suite of tests
90 | */
91 | def run(notifier: RunNotifier) {
92 |
93 | properties.properties.map({ propTuple =>
94 | propTuple match {
95 | case (desc, prop) => {
96 | val descObj = Description.createTestDescription(prop.getClass, desc)
97 |
98 | // TODO: is there a better way to do this? It seems that JUnit is not printing the actual name of the test case to the screen as it runs
99 | print("Running property: " + desc)
100 |
101 | notifier.fireTestStarted(descObj)
102 | SchkTest.check(Params(testCallback = consoleReporter chain (new CustomTestCallback(notifier, descObj))), prop)
103 | notifier.fireTestFinished(descObj)
104 | }
105 | }
106 | })
107 | }
108 |
109 | /**
110 | * Returns the number of tests that are expected to run when this ScalaTest Suite
111 | * is run.
112 | *
113 | * @return the expected number of tests that will run when this suite is run
114 | */
115 | override def testCount() = properties.properties.size
116 | }
--------------------------------------------------------------------------------
/scalacheck-integration-junit/src/test/scala/ScalaCheckJUnitRunnerExample.scala:
--------------------------------------------------------------------------------
1 | package com.company.junit
2 |
3 | import org.scalacheck.Prop._
4 | import org.junit.runner.RunWith
5 | import com.company.scalacheck.support.ScalaCheckJUnitPropertiesRunner
6 | import com.company.scalacheck.Rectangle
7 | import org.scalacheck.{Gen, Properties}
8 |
9 | /**
10 | * This is an example of a JUnit test suite implemented as a ScalaCheck Properties object, where all
11 | * unit test cases are properties that are evaluated as separate JUnit test cases
12 | *
13 | * The code is exactly the same as if the property was run using org.scalacheck.Test.check (and in
14 | * fact it can still be run like that) but using the @RunWith annotation with our custom runner
15 | * it can also be run as a JUnit suite
16 | */
17 | @RunWith(classOf[ScalaCheckJUnitPropertiesRunner])
18 | class ScalaCheckRunnerTest extends Properties("Rectangle property suite") {
19 | import RectangleGenerator._
20 |
21 | // This holds true and will be reported as a passed test by JUnit
22 | property("Test biggerThan") = forAll { (r1:Rectangle, r2:Rectangle) =>
23 | (r1 biggerThan r2) == (r1.area > r2.area)
24 | }
25 |
26 | // This does not hold true and will be reported as a test error by JUnit
27 | property("Failed test") = forAll {(a:Int) =>
28 | a == 1
29 | }
30 |
31 | // This holds true, and ScalaCheck will output the test data grouping to the console
32 | property("Test with collection of data") = forAll {(a:Int) =>
33 | (a > 0 && a <= 10) ==> collect(a) {
34 | 2 * a == a + a
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/scalacheck-integration-junit/src/test/scala/ScalaCheckJunitSupportExample.scala:
--------------------------------------------------------------------------------
1 | package com.company.junit
2 |
3 | import org.scalacheck.Properties
4 | import org.scalacheck.Prop._
5 | import com.company.scalacheck.support.ScalaCheckJUnitAdapter
6 |
7 | /**
8 | * This is an example of integrating ScalaCheck with JUnit using a custom adapter,
9 | * where the ScalaCheckJUnitAdapter provides a JUnit test case that will run all our
10 | * properties as a JUnit test case.
11 | *
12 | * The code is exactly the same as for vanilla ScalaCheck properties except for mixing in
13 | * the ScalaCheckJUnitAdapter into our class to automatically add JUnit integration
14 | *
15 | * The drawback of using this kind of integration compared to a native ScalaCheck runner
16 | * for JUnit is that all properties are run from a single test case.
17 | */
18 | class ScalaJUnitSupportTest extends Properties("JUnit Support Example") with ScalaCheckJUnitAdapter {
19 | property("valid property") = forAll { (a:String) =>
20 | a == a
21 | }
22 |
23 | property("failed property") = forAll { (a:String) =>
24 | // this example classifies input strings based on their length, and only for non-empty strings
25 | // shorter than 10 characters
26 | (a.length > 0 && a.length <= 10) ==> collect(a.length) {
27 | (a != "b")
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /project/boot/
3 | /project/plugins/project
4 | target/
5 | lib_managed/
6 | src_managed/
7 | test-output/
8 | *.iml
9 | *.db
10 |
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/README.txt:
--------------------------------------------------------------------------------
1 | Some examples of the integration of ScalaCheck with ScalaTest.
2 |
3 | The examples are built with SBT 0.11, which is requried to be in your path.
4 |
5 | In order to build and run the examples, use the following command:
6 |
7 | sbt reload test
8 |
9 | Or start the SBT command prompt and then type first "reload" and then "test".
10 |
11 | ScalaTest 1.6.1 and ScalaCheck 1.9 are required.
12 |
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/build.sbt:
--------------------------------------------------------------------------------
1 | name := "scalacheck-integration-scalatest"
2 |
3 | organization := "com.accenture"
4 |
5 | version := "1.0-SNAPSHOT"
6 |
7 | scalaVersion := "2.9.1"
8 |
9 | // package dependencies
10 | libraryDependencies ++= Seq(
11 | "org.scalatest" %% "scalatest" % "1.6.1",
12 | "org.scala-tools.testing" %% "scalacheck" % "1.9"
13 | )
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.11.1
2 |
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/src/test/scala/ArbitrarySpec.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalatest.PropSpec
4 | import org.scalatest.prop.PropertyChecks
5 | import org.scalatest.prop.Checkers
6 | import org.scalatest.matchers.MustMatchers
7 | import org.scalacheck.{Arbitrary, Gen, Properties}
8 | import org.scalacheck.Prop.forAll
9 |
10 | /**
11 | * Case class that we're going to test
12 | */
13 | case class Rectangle(val width:Double, val height:Double) {
14 | lazy val area = width * height
15 | lazy val perimeter = (2*width) + (2*height)
16 | def biggerThan(r:Rectangle) = (area > r.area)
17 | }
18 |
19 | /**
20 | * The generator and arbitray objects that generate entities of our case class are
21 | * exactly the same as when using ScalaCheck
22 | */
23 | object RectangleGenerator {
24 | // generator for the Rectangle case class
25 | val rectangleGen:Gen[Rectangle] = for {
26 | height <- Gen.choose(0,9999)
27 | width <- Gen.choose(0,9999)
28 | } yield(Rectangle(width, height))
29 |
30 | // Arbitrary generator of rectangles
31 | implicit val arbRectangle: Arbitrary[Rectangle] = Arbitrary(rectangleGen)
32 | }
33 |
34 | /**
35 | * This is a ScalaTest specification that tests a case class with ScalaCheck properties. This
36 | * spec is using the ScalaTest style of property tests (by mixing in the PropertyChecks) trait;
37 | * it is also possible to use ScalaCheck style property checks by importing the Checkers trait
38 | * instead. Please note that the forAll method below is provided by PropertyChecks and it's not
39 | * ScalaCheck's!
40 | *
41 | * The main difference is that we can use ScalaTest's matchers (using the MustMatchers
42 | * trait in this case).
43 | */
44 | class ArbitraryRectangleSpec extends PropSpec with PropertyChecks with MustMatchers {
45 | import com.company.scalacheck.RectangleGenerator._
46 |
47 | property("A rectangle should correctly calculate its area") {
48 | forAll { (r:Rectangle) =>
49 | r.area must be (r.width * r.height)
50 | }
51 | }
52 | property("A rectangle should be able to identify which rectangle is bigger") {
53 | forAll { (r1:Rectangle, r2:Rectangle) =>
54 | (r1 biggerThan r2) must be(r1.area > r2.area)
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * This is the same specification as described above, but written using
61 | * ScalaCheck style property checks via the Checkers trait
62 | */
63 | class ArbitraryRectangleWithCheckersSpec extends PropSpec with Checkers {
64 | import com.company.scalacheck.RectangleGenerator._
65 |
66 | property("A rectangle should correctly calculate its area") {
67 | check(forAll { (r:Rectangle) =>
68 | r.area == (r.width * r.height)
69 | })
70 | }
71 | property("A rectangle should be able to identify which rectangle is bigger") {
72 | check(forAll { (r1:Rectangle, r2:Rectangle) =>
73 | (r1 biggerThan r2) == (r1.area > r2.area)
74 | })
75 | }
76 | }
--------------------------------------------------------------------------------
/scalacheck-integration-scalatest/src/test/scala/SimpleSpec.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalatest.PropSpec
4 | import org.scalatest.prop.{PropertyChecks, Checkers}
5 | import org.scalatest.matchers.ShouldMatchers
6 | import org.scalacheck.Prop._
7 |
8 | /**
9 | * This is an example spec written in ScalaTest using ScalaCheck property checks.
10 | *
11 | * An advantage of using ScalaTest is that we can use its more readable matchers
12 | * within our properties, i.e. "should be" instead of "==".
13 | */
14 | class SimplePropertySpec extends PropSpec with PropertyChecks with ShouldMatchers {
15 | /**
16 | * The simplest property check
17 | */
18 | property("String should append each other with the concat method") {
19 | forAll { (a:String, b:String) =>
20 | a.concat(b) should be (a + b)
21 | }
22 | }
23 |
24 | /**
25 | * An example of a failing property. The console reporting looks somewhat different
26 | * to how it would look like if plain ScalaCheck was used
27 | */
28 | property("This property should fail") {
29 | forAll { (a: String, b: String) =>
30 | a.length + b.length should equal ((a + b).length + 1) // Should fail
31 | }
32 | }
33 |
34 | /**
35 | * ScalaTest's whenever function replaces ScalaCheck's ==> function for conditional.
36 | * property values
37 | */
38 | property("Reverse non-empty strings correctly") {
39 | forAll { (a:String) =>
40 | whenever(a.length > 0) {
41 | a.charAt(a.length-1) should be(a.reverse.charAt(0))
42 | }
43 | }
44 | }
45 | }
46 |
47 | /**
48 | * This is the same specification above but written using the Checkers trait
49 | */
50 | class SimplePropertyCheckersSpec extends PropSpec with Checkers {
51 |
52 | property("String should append each other with the concat method") {
53 | check(forAll { (a:String, b:String) =>
54 | a.concat(b) == (a + b)
55 | })
56 | }
57 |
58 | property("This property should fail") {
59 | check(forAll { (a: String, b: String) =>
60 | a.length + b.length == ((a + b).length + 1) // Should fail
61 | })
62 | }
63 |
64 | property("Reverse non-empty strings correctly") {
65 | check(forAll { (a:String) =>
66 | (a.length > 0) ==> (a.charAt(a.length-1) == (a.reverse.charAt(0)))
67 | })
68 | }
69 | }
--------------------------------------------------------------------------------
/scalacheck-integration-specs/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /project/boot/
3 | /project/plugins/project
4 | target/
5 | lib_managed/
6 | src_managed/
7 | test-output/
8 | *.iml
9 | *.db
10 |
--------------------------------------------------------------------------------
/scalacheck-integration-specs/README.txt:
--------------------------------------------------------------------------------
1 | Some examples of the integration of ScalaCheck with Scala Specs.
2 |
3 | The examples are built with SBT 0.11, which is requried to be in your path.
4 |
5 | In order to build and run the examples, use the following command:
6 |
7 | sbt reload test
8 |
9 | Or start the SBT command prompt and then type first "reload" and then "test".
10 |
11 | Specs2 1.5 and ScalaCheck 1.9 are required.
--------------------------------------------------------------------------------
/scalacheck-integration-specs/build.sbt:
--------------------------------------------------------------------------------
1 | name := "scalacheck-integration-specs"
2 |
3 | organization := "com.accenture"
4 |
5 | version := "1.0-SNAPSHOT"
6 |
7 | scalaVersion := "2.9.1"
8 |
9 | // package dependencies
10 | libraryDependencies ++= Seq(
11 | "org.specs2" %% "specs2" % "1.5",
12 | "org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test"
13 | )
14 |
15 | // use the specs2 runner
16 | testFrameworks += TestFrameworks.Specs2
--------------------------------------------------------------------------------
/scalacheck-integration-specs/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.11.1
2 |
--------------------------------------------------------------------------------
/scalacheck-integration-specs/src/test/scala/ArbitrarySpec.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.scalacheck.{Arbitrary, Gen}
4 | import org.specs2.mutable._
5 | import org.specs2.ScalaCheck
6 |
7 | /**
8 | * Case class that we're going to test
9 | */
10 | case class Rectangle(val width:Double, val height:Double) {
11 | lazy val area = width * height
12 | lazy val perimeter = (2*width) + (2*height)
13 | def biggerThan(r:Rectangle) = (area > r.area)
14 | }
15 |
16 | /**
17 | * In this example, the arbitrary generator is kept in a separate object, but the generator and
18 | * arbitrary code are exactly the same as when using "pure" ScalaCheck
19 | */
20 | object RectangleGenerator {
21 | // generator for the Rectangle case class
22 | val arbRectangleGen:Gen[Rectangle] = for {
23 | height <- Gen.choose(0,9999)
24 | width <- Gen.choose(0,9999)
25 | } yield(Rectangle(width, height))
26 |
27 | // Arbitrary generator of rectangles
28 | implicit val arbRectangle: Arbitrary[Rectangle] = Arbitrary(arbRectangleGen)
29 | }
30 |
31 |
32 | class ArbitraryRectangleSpec extends Specification with ScalaCheck {
33 | import com.company.scalacheck.RectangleGenerator._
34 |
35 | "Rectangle" should {
36 | "correctly calculate its area" ! check { (r:Rectangle) =>
37 | r.area == r.width * r.height
38 | }
39 | "be able to identify which rectangle is bigger" ! check { (r1:Rectangle, r2:Rectangle) =>
40 | (r1 biggerThan r2) == (r1.area > r2.area)
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/scalacheck-integration-specs/src/test/scala/SimpleSpec.scala:
--------------------------------------------------------------------------------
1 | package com.company.scalacheck
2 |
3 | import org.specs2.mutable._
4 | import org.specs2.ScalaCheck
5 |
6 | /**
7 | * Basic Spec2 specification
8 | */
9 | class HelloWorldSpec extends Specification {
10 |
11 | "The 'Hello world' string" should {
12 | "contain 11 characters" in {
13 | "Hello world" must have size (11)
14 | }
15 | "start with 'Hello'" in {
16 | "Hello world" must startWith("Hello")
17 | }
18 | "end with 'world'" in {
19 | "Hello world" must endWith("world")
20 | }
21 | }
22 | }
23 |
24 | /**
25 | * Sample specification that uses a couple of simple ScalaCheck properties.
26 | * The properties are simplistic and rather useless
27 | */
28 | class SimplePropertySpec extends Specification with ScalaCheck {
29 | "Strings" should {
30 | "append after each other with the concat method" ! check { (a:String, b:String) =>
31 | a.concat(b) == a + b
32 | }
33 | "reverse correctly for non-empty strings" ! check {(a:String) =>
34 | // do it only for string lengths longer than 0, otherwise the property doesn't hold
35 | (a.length > 0) ==> (a.charAt(a.length-1) == a.reverse.charAt(0))
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------