В этом уроке вы узнаете о тестировании с помощью Specs, Scala фреймворк для Разработки, основанная на функционировании.
Давайте сразу посмотрим код.
import org.specs._ object ArithmeticSpec extends Specification { "Arithmetic" should { "add two numbers" in { 1 + 1 mustEqual 2 } "add three numbers" in { 1 + 1 + 1 mustEqual 3 } } }
Arithmetic означает, что System Under Specification(Система имеет спецификацию)
add — это контекст.
add two numbers и add three numbers — это примеры.
mustEqual
отражает наше ожидание
1 mustEqual 1
является общим заменителем ожидания, прежде чем мы начнем писать реальные тесты. Все примеры должны иметь по крайней мере одно ожидание.
Заметили, что оба теста содержат add
в своем имени? Мы избавиться от этого благодаря вложенным ожиданиям.
import org.specs._ object ArithmeticSpec extends Specification { "Arithmetic" should { "add" in { "two numbers" in { 1 + 1 mustEqual 2 } "three numbers" in { 1 + 1 + 1 mustEqual 3 } } } }
object ExecSpec extends Specification { "Mutations are isolated" should { var x = 0 "x equals 1 if we set it." in { x = 1 x mustEqual 1 } "x is the default value if we don't change it" in { x mustEqual 0 } } }
"my system" should { doBefore { resetTheSystem() /** определенная пользователем функция сброса */ } "mess up the system" in {...} "and again" in {...} doAfter { cleanThingsUp() } }
ЗАМЕЧАНИЕ doBefore
/doAfter
будут работать только с черновыми примерами.
doFirst
/doLast
предназначены во время установки (здесь нужен пример, я не использую их)
"Foo" should { doFirst { openTheCurtains() } "test stateless methods" in {...} "test other stateless methods" in {...} doLast { closeTheCurtains() } }
У вас есть данные, и вы хотите убедиться, что они верные. Давайте рассмотрим наиболее часто используемые конструкции сравнения. (Смотрите также «Matchers Guide»:https://code.google.com/p/specs/wiki/MatchersGuide)
Вы уже видели несколько примеров с mustEqual.
1 mustEqual 1 "a" mustEqual "a"
Равенство ссылок, равенство значений.
val numbers = List(1, 2, 3) numbers must contain(1) numbers must not contain(4) numbers must containAll(List(1, 2, 3)) numbers must containInOrder(List(1, 2, 3)) List(1, List(2, 3, List(4)), 5) must haveTheSameElementsAs(List(5, List(List(4), 2, 3), 1))
map must haveKey(k) map must notHaveKey(k) map must haveValue(v) map must notHaveValue(v)
a must beGreaterThan(b) a must beGreaterThanOrEqualTo(b) a must beLessThan(b) a must beLessThanOrEqualTo(b) a must beCloseTo(b, delta)
a must beNone a must beSome[Type] a must beSomething a must beSome(value)
a must throwA[WhateverException]
Запись короче чем try catch с его попыткой выброса исключения в теле блока.
Вы можете выдать специальное сообщение
a must throwA(WhateverException("message"))
Вы можете также поймать определенное исключение:
a must throwA(new Exception) like { case Exception(m) => m.startsWith("bad") }
import org.specs.matcher.Matcher
"A matcher" should { "be created as a val" in { val beEven = new Matcher[Int] { def apply(n: => Int) = { (n % 2 == 0, "%d is even".format(n), "%d is odd".format(n)) } } 2 must beEven } }
Контракт вернет кортеж, содержащий запись о том верно ожидание или нет, и сообщение когда оно верно или наоборот.
case class beEven(b: Int) extends Matcher[Int]() { def apply(n: => Int) = (n % 2 == 0, "%d is even".format(n), "%d is odd".format(n)) }
Используя case класс, вы делаете его менее специализированным.
import org.specs.Specification import org.specs.mock.Mockito abstract class Foo[T] { def get(i: Int): T } object MockExampleSpec extends Specification with Mockito { val m = mock[Foo[String]] m.get(0) returns "one" m.get(0) there was one(m).get(0) there was no(m).get(1) }
Смотрите также: «Использование Mockito»:https://code.google.com/p/specs/wiki/UsingMockito
Шпионы могут также быть использованы для того, чтобы сделать некоторую «частичную подделку» реальных объектов:
val list = new LinkedList[String] val spiedList = spy(list) // методы могут выдать ошибку при использовании шпиона spiedList.size returns 100 // другие методы также могут быть использованы spiedList.add("one") spiedList.add("two") // и проверка может происходить с помощью шпиона there was one(spiedList).add("one")
Однако, работа со шпионами может быть сложной:
// если список пуст, то бросается исключение IndexOutOfBoundsException spiedList.get(0) returns "one"
doReturn
должен быть исопльзован в этом случае:
doReturn("one").when(spiedList).get(0)
> test-only com.twitter.yourservice.UserSpec
Запустится только данный спек.
> ~ test-only com.twitter.yourservice.UserSpec
Будет запускаться тест в цикле, с каждой модификацией файла будет срабатывать триггер и будет запускаться тест.