├── .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 | } --------------------------------------------------------------------------------