Using Mockito

It’s better to eliminate, or at least reduce dependencies, as much as possible. You can then temporarily replace essential intrinsic dependencies with stubs or mocks to get rapid feedback from unit tests.

If you’re used to EasyMock, JMock, or Mockito with JUnit, you can readily use them with ScalaTest as well. Let’s explore an example that will illustrate the use of Mockito with ScalaTest.

Tests with Functional Style

Let’s start with a series of tests for a score method that’s used in a guessing game. This method will return a total score based on the number of vowels—each vowel will receive one point whereas other characters will earn two points.

In the previous example we used a trait to hold the instance being tested. ScalaTest also provides a BeforeAndAfter mixing that will invoke a before and a after method for each test method. There’s yet another technique we could use—one that’s functional in style—as in this test:

 
import​ org.scalatest.{FlatSpec, Matchers}
 
 
class​ WordScorerTest ​extends​ FlatSpec ​with​ Matchers {
 
 
def​ withWordScorer(test: WordScorer => ​Unit​) = {
 
val​ wordScorer = ​new​ WordScorer()
 
 
test(wordScorer)
 
}
 
 
"score"​ should ​"return 0 for an empty word"​ in {
 
withWordScorer { wordScorer => wordScorer.score(​""​) should be (0) }
 
}
 
 
"score"​ should ​"return 2 for word with two vowels"​ in {
 
withWordScorer { _.score(​"ai"​) should be (2) }
 
}
 
 
"score"​ should ​"return 8 for word with four consonants"​ in {
 
withWordScorer { _.score(​"myth"​) should be (8) }
 
}
 
 
"score"​ should ​"return 7 for word with a vowel and three consonants"​ in {
 
withWordScorer { _.score(​"that"​) should be (7) }
 
}
 
}

The withWordScorer method is a helper and not a test. It accepts a test function as its parameter and passes an instance of a WordScorer, the class under test, to the test function.

In the first test, we call the method withWordScorer and pass a function value to it. The function value is essentially the test code; it receives an instance of WordScorer and asserts that its score method returns a 0 when an empty String is passed.

In the remaining tests we use the withWordScorer method in the same way, with one difference. Instead of the more verbose explicit name wordScorer, we use an underscore (_) to refer to the parameter that the function value receives.

Let’s implement the score method to satisfy these tests.

 
class​ WordScorer() {
 
private​ ​val​ VOWELS = ​List​('a', 'e', 'i', 'o', 'u')
 
 
def​ score(word: ​String​) = {
 
(0 /: word) { (total, letter) =>
 
total + (​if​ (VOWELS.contains(letter)) 1 ​else​ 2) }
 
}
 
}

To compile and run the tests, use the following commands:

 
scalac -d classes -classpath $SCALA_TEST_JAR
 
WordScorer.scala WordScorerTest.scala
 
scala -classpath $SCALA_TEST_JAR:classes org.scalatest.run WordScorerTest

Let’s ensure all tests are passing by running the tests:

 
Run starting. Expected test count is: 4
 
WordScorerTest:
 
score
 
- should return 0 for an empty word
 
score
 
- should return 2 for word with two vowels
 
score
 
- should return 8 for word with four consonants
 
score
 
- should return 7 for word that with a vowel and three consonants
 
Run completed in 181 milliseconds.
 
Total number of tests run: 4
 
Suites: completed 1, aborted 0
 
Tests: succeeded 4, failed 0, canceled 0, ignored 0, pending 0
 
All tests passed.

We have some basic features for the method being tested. Now let’s now take it to the next level by bringing in a dependency.

Creating a Mock

Let’s add a new requirement to the problem on hand. If the spelling of the given word is incorrect the score method should return a 0; otherwise it will return a valid score.

Now we need to change the method to use a spell checker, but which one? A quick Google search for “Java Spell Checkers” should convince you that it’s not a decision you want to make so quickly—there are that many out there. Our solution is to mock away the spell checker to keep the focus on the score method while getting the rapid feedback from the tests.

Before we work on an incorrect spelling, we want to ensure the current tests will continue to pass with the inclusion of a spell checker. Let’s make a series of small changes for that.

First, we need an interface—a trait in Scala, of course—to abstract a spell checker:

 
trait​ SpellChecker {
 
def​ isCorrect(word: ​String​): ​Boolean
 
}

Now, let’s modify the withWordScorer method in the test class to create a mock for the SpellChecker.

 
import​ org.scalatest.{FlatSpec, Matchers}
 
import​ org.mockito.Mockito._
 
import​ org.mockito.Matchers.anyString
 
 
class​ WordScorerTest ​extends​ FlatSpec ​with​ Matchers {
 
 
def​ withWordScorer(test: WordScorer => ​Unit​) = {
 
val​ spellChecker = mock(classOf[SpellChecker])
 
when(spellChecker.isCorrect(anyString)).thenReturn(true)
 
val​ wordScorer = ​new​ WordScorer(spellChecker)
 
 
test(wordScorer)
 
 
verify(spellChecker, times(1)).isCorrect(anyString())
 
}
 
 
//No change to the tests, same as in the previous version
 
}

Using the mock method of Mockito we create a mock object for the SpellChecker interface. All the current tests have words that are spelled correctly. To satisfy these tests, we instruct the mock, using the when method, to return true for any word that’s given as an argument to the isCorrect method. Then we pass the mock instance of SpellChecker to the constructor of WordScorer. Once we return from the test call, to which the instance of WordScorer is passed, we ask Mockito to verify that the isCorrect method on the mock was called exactly once, no matter what the string argument was.

We only changed the withWordScorer method; the tests remain the same as in the previous version. Since we’re passing an argument to the constructor of WordScorer now, we have to change the class accordingly. Also, for the tests to pass, the score method has to use the SpellChecker’s isCorrect method. Let’s make the minimum code change to make the tests pass.

 
class​ WordScorer(​val​ spellChecker: SpellChecker) {
 
private​ ​val​ VOWELS = ​List​('a', 'e', 'i', 'o', 'u')
 
 
def​ score(word: ​String​) = {
 
spellChecker.isCorrect(word)
 
(0 /: word) { (total, letter) =>
 
total + (​if​ (VOWELS.contains(letter)) 1 ​else​ 2) }
 
}
 
}

The WordScorer class receives and stores a reference to an instance of SpellChecker. The score method merely invokes the isCorrect method to satisfy the tests.

We need to include the Mockito library to successful compile and run the tests. Download Mockito[8] and set up the environment variable $MOCKITO_JAR to refer to the appropriate JAR file. Then run the following commands:

 
scalac -d classes -classpath $SCALA_TEST_JAR:$MOCKITO_JAR
 
WordScorer.scala SpellChecker.scala WordScorerTest.scala
 
scala -classpath $SCALA_TEST_JAR::$MOCKITO_JAR:classes
 
org.scalatest.run WordScorerTest

Now that we’ve added the mock and modified the class, let’s ensure all tests are still passing by running them:

 
Run starting. Expected test count is: 4
 
WordScorerTest:
 
score
 
- should return 0 for an empty word
 
score
 
- should return 2 for word with two vowels
 
score
 
- should return 8 for word with four consonants
 
score
 
- should return 7 for word that with a vowel and three consonants
 
Run completed in 316 milliseconds.
 
Total number of tests run: 4
 
Suites: completed 1, aborted 0
 
Tests: succeeded 4, failed 0, canceled 0, ignored 0, pending 0
 
All tests passed.

As the final step, let’s write the test for an incorrect spelling.

 
"score"​ should ​"return 0 for word with incorrect spelling"​ in {
 
val​ spellChecker = mock(classOf[SpellChecker])
 
when(spellChecker.isCorrect(anyString)).thenReturn(false)
 
val​ wordScorer = ​new​ WordScorer(spellChecker)
 
 
wordScorer.score(​"aoe"​) should be (0)
 
verify(spellChecker, times(1)).isCorrect(anyString())
 
}

Since we need the SpellChecker mock’s isCorrect method to return false, in this newly added test we create a new mock instead of reusing the mock created in withWordScorer. Running the tests now will result in a failure since the score method of WordScorer is currently ignoring the result of the call to isCorrect. Let’s change that.

 
def​ score(word: ​String​) = {
 
if​(spellChecker.isCorrect(word))
 
(0 /: word) { (total, letter) =>
 
total + (​if​ (VOWELS.contains(letter)) 1 ​else​ 2) }
 
else
 
0
 
}

The score method now returns a valid score only if the spelling for the given word is correct; otherwise it returns 0. Let’s run the tests once more, using the same commands as used before, and see all tests, including the newly added one pass.

 
Run starting. Expected test count is: 5
 
WordScorerTest:
 
score
 
- should return 0 for an empty word
 
score
 
- should return 2 for word with two vowels
 
score
 
- should return 8 for word with four consonants
 
score
 
- should return 7 for word that with a vowel and three consonants
 
score
 
- should return 0 for word with incorrect spelling
 
Run completed in 208 milliseconds.
 
Total number of tests run: 5
 
Suites: completed 1, aborted 0
 
Tests: succeeded 5, failed 0, canceled 0, ignored 0, pending 0
 
All tests passed.

You saw how easy it is to use the Java mocking libraries from ScalaTest. Now there’s nothing to stop you from benefiting from the rapid feedback of unit tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.136.235