Scalafication of an Equity Quote Application Part 2
23 July 2011 2 comments
Reading time:
9 minutes
Word count:
1834
In Part 1 of this article, we discussed the Java language form of an exercise project. The task was to create an equity quote application that retrieved stock prices from a public web service Yahoo! Finance.
Let me suggest a really good process of adopting Scala inside the organisation: take the unit tests written in Java and port them to Scala. The Java implementation does not change and one can learn Scala principles by applying test first and test driven principle.
In the twenty tens, as a modern software practitioner, you must be familiar with JUnit and also the Hamcrest matchers, which are unit test framework by Kent Beck and fluent assertion matchers respectively. The pun was deliberately intended as you will read in the rest of this article.
Just because you start writing Scala does not mean that now you have to give up unit testing (JUnit or TestNG). You can also use Hamcrest Matchers directly from Scala. For example here a unit test:
import org.junit._ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers._ @Test class SimpleTest { def shouldVerifyTheEssence() { val expected = List( 1,1,2,3,5,8 ) val actual = flatten( List(List(1, 1), 2, List(3, List(5, 8)))) ) assertThat( actual, is(expected ) } }
Notice, that you can use annotations in Scala as well. Of course, the immutable variable references are unnecessary, however the purpose of the unit test should be clear to read. Your pairing partner should be able to understand your intent and can agree with you that it is concise. The method flatten reduces a nest list collection to a single list collection. The Hamcrest matchers are useful for fluent API programming, such that the code almost reads like an English description. For more information on Hamcrest, see the tutorial.
Let me introduce you to Scala Test, which is a fluent API testing framework written by Bill Venners, and it can be called from JUnit and it has support for behaviour driven design (BDD). You can use the assertion API from the JUnit inside a ScalaTest like this:
import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers import org.junit.Assert._ import org.scalatest.junit.JUnitRunner import org.junit.runner.RunWith @RunWith(classOf[JUnitRunner]) class FibonacciSeriesSpec extends FlatSpec with ShouldMatchers { "A Fibonacci function" should "establish a mathematical series" in { val expected = List( 0, 1,1,2,3,5,8,13,21, 34, 55, 89, 144, 233, 377, 610, 987 ]; expected.foreach( x => assertEquals( x, fibonacci(x) ) } }
Behind the scenes of this examples are a few bits of knowledge:
- This is the short form of running a JUnit test with ScalaTest, because I prefer to use the JUnit Runner and because it avoids writing the companion object. With JUnit (or TestNG) runners the test can be launched from popular Java tools like SBT, Maven or an IDE.
- The specification is essentially a JRuby (RSpec) syntax in keeping with the BDD style (See Dan North on Introduction BDD)
- Demonstrates that domain specific language can be written in Scala.
- A fluent API in Scala can be supported by Scala powerful implicit conversions
- The unit tests are readable, the expected behaviour of the target can be literally described in the language. Once again, it about Scala’s exceptional ability to allow developers to express their intent in a concise fashion.
- ScalaTest allows you to reuse traditional style of unit testing too. You can find example of a JUnit 4 style in Scala here.
There is another Scala testing framework for BDD called Specification, which can be found here
https://code.google.com/p/specs. Here is an example for writing an executable specification with this framework very quickly.
import org.specs._ class MySpec extends SpecificationWithJUnit { "This wonderful system" should { "save the world" in { val list = Nil list must beEmpty println ("Done with the specification") } } }
(UPDATED: This framework has been superseded by second version called Specs 2 https://specs2.org/. Thanks to Eric Torreborre for the corrections.)
With both ScalaTest and Specification, it is possible to use a mixture of the fluent and traditional assertion API in the unit tests. The quote retriever examples that I developed used ScalaTest, I prefer to use the BDD style rather than the traditional JUnit style, because it reads easier to a non-coder (or a line manager ;-).
ScalaTest has a few specification styles that support alternative BDD styles. There are the FeatureSpec, FlatSpec, and my own favourite WordSpec. These styles are mixed into tests as Scala traits. In order to complete the BDD style you normally mix in the ShouldMatchers, and therefore you do not need the Java styled JUnit assertion classes or the Hamcrest Macthers.
This is an example of the FeatureSpec style.
import org.scalatest.Spec import org.scalatest.matchers.ShouldMatchers import scala.collection.mutable.Stack class StackSpec extends Spec with ShouldMatchers { describe("A Stack") { describe("(when empty)") { val stack = new Stack[Int] it("should be empty") { stack should be ('empty) } it("should complain when popped") { evaluating { stack.pop() } should produce [NoSuchElementException] } } } }
This is an example of the WordSpec style.
import org.scalatest.WordSpec import org.scalatest.matchers.ShouldMatchers import scala.collection.mutable.Stack class StackSpec extends WordSpec with ShouldMatchers { "A Stack" when { "empty" should { val stack = new Stack[Int] "be empty" in { stack should be ('empty) } "add one item and remove one item" in { stack.push(1974) stack should not be ('empty) stack.pop() should be 1974 stack should be ('empty) } "complain when popped" in { evaluating { stack.pop() } should produce [NoSuchElementException] } } } }
In the Java world, I discovered years ago, the Mockito framework as a great mocking tool. Did you know that you can use Mockito in a Scala as well, and therefore with ScalaTest? Here is how. Here follows the first the unit test that uses ScalaTest to verify the operation of the stock quote retriever.
package uk.co.xenonique.stockquoteapp_scala import org.scalatest.WordSpec import org.scalatest.mock.MockitoSugar import org.scalatest.junit.JUnitRunner import org.scalatest.matchers.ShouldMatchers import org.mockito.Mockito._ import org.junit.runner.RunWith @RunWith(classOf[JUnitRunner]) class StockQuoteAppTest extends WordSpec with ShouldMatchers with MockitoSugar { "The quote retriever" should { "get stock quotes" in { val repo: QuoteRepository = new QuoteRepositoryKeyValueStore() val mockRetriever: QuoteRetriever = mock[ QuoteRetriever ] val expected1 = new StockQuote("APPL", "100.24", "100.20") when( mockRetriever.getStockQuote( expected1.symbol )).thenReturn(expected1) val expected2 = new StockQuote("PILG", "725.96", "721.32") when( mockRetriever.getStockQuote( expected2.symbol )).thenReturn(expected2) val app = new StockQuoteApp( mockRetriever, repo ) app.findAndUpdateStockQuote(expected1.symbol) repo.get( expected1.symbol).get should be === expected1 app.findAndUpdateStockQuote(expected2.symbol) repo.get( expected2.symbol).get should be === expected2 repo.isEmpty() should be === false repo.size() should be === 2 verify(mockRetriever).getStockQuote(expected1.symbol) verify(mockRetriever).getStockQuote(expected2.symbol) } } "The quote retriever" should { "get stock quotes with one changed" in { val repo: QuoteRepository = new QuoteRepositoryKeyValueStore() val mockRetriever: QuoteRetriever = mock[QuoteRetriever] val expected1 = new StockQuote("APPL", "100.24", "100.20") when( mockRetriever.getStockQuote( expected1.symbol )).thenReturn(expected1) val expected2 = new StockQuote("PILG", "725.96", "721.32") when( mockRetriever.getStockQuote( expected2.symbol )).thenReturn(expected2) val app = new StockQuoteApp( mockRetriever, repo ) app.findAndUpdateStockQuote(expected1.symbol) repo.get( expected1.symbol).get should be === expected1 app.findAndUpdateStockQuote(expected2.symbol) repo.get( expected2.symbol).get === expected2 repo.isEmpty() should be === false repo.size() should be === 2 val expected3 = new StockQuote("APPL", "101.50", "101.46") when( mockRetriever.getStockQuote( expected3.symbol )).thenReturn(expected3) app.findAndUpdateStockQuote(expected3.symbol) repo.get( expected3.symbol).get should be === expected3 repo.isEmpty() should be === false repo.size() should be === 2 verify(mockRetriever, times(2) ).getStockQuote(expected1.symbol) verify(mockRetriever, times(1) ).getStockQuote(expected2.symbol) } } }
Oh yes, I forgot to mention that ScalaTest has a special method called ===
. In Scala, identifiers can be mixed characters, and the triple equals serves as an assertion equality operator to compare expected and actual results. This operator aid debugging and when a test fails prints out the expected and actual values and more important where in the stacktrace the failure occured. In my opinion, for the negative cases, ScalaTest needs an associative operator !===
.
In the unit test, please note, I have duplicated some assertions with the should matchers and also the equality operator in ordered to prove that the two forms are the same. For mathematical derived testing for finance or science, I think that I would prefer the “===” operator rather the long english form. It will depend on your style of testing, but the fact that ScalaTest allows different art forms, in itself, is great.
repo.isEmpty() should be === false repo.isEmpty() should be !=== true // Does not exist in ScalaTest 1.3 assert ( repo.size() === false )
ScalaTest has good documentation on all of the available types of ShouldMatchers here. You will find relational operations like greaterThan
or lessThan
over there.
Here follows the second unit test that verifies the operation of the quote key value repository.
package uk.co.xenonique.stockquoteapp_scala import org.scalatest.WordSpec import org.scalatest.matchers.ShouldMatchers import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class QuoteRepositoryKeyValueTest extends WordSpec with ShouldMatchers { "quote repository key value store" should { "initialise well" in { val store:QuoteRepository = new QuoteRepositoryKeyValueStore() assert( store.isEmpty() === true ) assert( store.size() === 0 ) store.isEmpty should be === true store.size() should be === 0 } } "quote repository key value store" should { "store and retrieve stock quotes" in { val store = new QuoteRepositoryKeyValueStore() val expected1 = new StockQuote("PILGRIM", "123.45", "123.12" ) store.store(new StockQuote(expected1)) assert( store.isEmpty() === false ) assert( store.size() === 1 ) store.isEmpty should be === false store.size() should be === 1 assert( store.get("PILGRIM").get === expected1 ) val expected2 = new StockQuote("PILGRIM", "555.55", "555.12" ) store.store(expected2) assert( store.isEmpty() === false ) assert( store.size() === 1 ) store.get("PILGRIM").get should not be expected1 store.get("PILGRIM").get should be === expected2 assert( store.get("PILGRIM").get === expected2 ) } } "quote repository key value store" should { "store and retrieve multiple stock quotes" in { val store = new QuoteRepositoryKeyValueStore() assert( store.isEmpty() === true ) assert( store.size() === 0 ) val quotes = List( new StockQuote( "LYG", "4.020", "4.015" ), new StockQuote( "RBS.L", "39.58", "39.48" ), new StockQuote( "HSBC.L", "646.65", "646.62" ), new StockQuote( "ORCL", "27.75", "27.74" ) ) for (quote <- quotes) store.store(quote) assert( store.size() === quotes.length ) assert( store.isEmpty() === false) for (quote <- quotes) { assert( store.contains(quote.symbol ) === true ) assert( store.get(quote.symbol ).get == quote ) } val symbols = store.getSymbols() assert( symbols.size === quotes.size ) for ( quote <- quotes) { assert( symbols.contains(quote.symbol ) === true ) } store.clear() assert( store.isEmpty() === true ) assert( store.size() === 0 ) } } }
In part 3, we will see the code implementation of quote retriever in Scala.
Learn a new language! Maintenance mode programmers please go elsewhere!
Word.