Especificando validações com Scala, Specs e Lift – Parte II
No último Post tivemos uma visão inicial do uso de Specs, BDD e um exemplo de utilização do framework. Mas como você deve ter percebido, as especificações eram blocos de códigos ainda soltos no Post. Specifications são objects ou classes Scala, vejamos como declarar a especificação do post anterior, configurar uma sessão Lift, criar um contexto de execução das especificações, e por fim estender o framework Specs com Matchers personalizados.
object TodoSpec extends Specification with Contexts {
"Create/Update a ToDo item" should {
//... spec from last post.
}
//Inicia um sessão Lift
val session = new LiftSession("", StringHelpers.randomString(20),
new MockHttpSession, null)
//Executa a dentro da sessão Lift
def inSession(a: => Any) = {
S.initIfUninitted(session) { a }
}
//Novo usuário para execução da especificação
def loginUser = inSession {
val user = User.create
user.email("tester@gmail.com").password("xxxxxx")
user.save
User.logUserIn(user)
}
//Contexto de execuão da Especificação
new SpecContext {
//Estabelece Conexão com o banco antes da execução.
beforeSpec {
if (!DB.jndiJdbcConnAvailable_?)
DB.defineConnectionManager(DefaultConnectionIdentifier,
DBVendor)
loginUser //Acontece o login do usuário
}
aroundExpectations(inSession(_))
//Remove o usuário de teste
afterSpec {
val user = User.find(By(User.email, "tester@gmail.com"))
User.delete_!(user.open_!)
}
}
}
class TodoSpecTest extends JUnit4(TodoSpec)
Uma especificação é um objeto que extends a classe org.specs.Specification. Specification oferece um conjunto de conversores implícitos para Strings, por isso os métodos should, in e >> aparentam serem invocados a partir das strings que descrevem a especificação.
Como todo teste que deve acontecer dentro de um contexto (jndi, spring, hibernate, ejb3, etc), precisamos de um código extra para usarmos recursos do Lift e definir um contexto para a execução da especificação. Por isso, aqui usamos a trait Contexts e criamos uma nova instância de SpecContext com os métodos beforeSpec fazendo a conexão com a base de dados e aroundExpectations garantindo que a especificação ocorra dentro da sessão Lift criada.
Com Specs, você pode visulizar o resultado das execuções na sua IDE como se fossem testes JUnit. Por isso a TodoSpec é um objecto e há uma classe TodoSpecTest que extends org.specs.runner.JUnit4. O objeto da especificação é passado como construtor do runner de JUnit4. Assim temos a execução da especificação integrada com a IDE. Veja outras formas executá-las aqui.
Usamos o próprio Lift Test Kit uma instância de HttpSession (new MockHttpSession), assim podemos instanciar corretamente uma sessão Lift.
Para a execução dos testes vamos precisar das dependências abaixo. Lembre-se de instalar no seu repositório local Maven a versão SNAPSHOT do Specs 1.6.1 encontrado aqui.
<dependency>
<groupId>org.scala-tools.testing</groupId>
<artifactId>specs</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-testkit</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
Outro ponto importante do framework Specs, é a sua extensibilidade. Podemos criar Matchers tão poderosos quanto queiramos para oferecer um melhor suporte à linguagem única utilizada no negócio do sistema. Supondo que a especificação descreva que um usuário logado no sistema deve ser um usuário Gmail, podemos criar nosso próprio matcher para tornar nossa especificação mais natural. Vamos adicionar o seguinte sistema para especificar o comportamento do nosso matcher, facilitando o entendimento:
"A logged user" should {
"be a Gmail user" in {
val user = User.currentUser.open_!
user.email.asString must beAtGmail
}
}
beAtGamil é um matcher definido em um object que pode conter diversos matchers customizados. Até aqui temos apenas um matcher:
object CustomMatcherSpec extends Specification {
"beAtGmail" can {
"assert users at Gmail" in {
"paulosuzart@gmail.com" must beAtGmail
}
}
}
object CustomMatcher {
import java.util.regex.Pattern
val beAtGmail = new Matcher[String]{
def apply(m : => String) = {
val pattern = Pattern.compile(".+\\@gmail.com")
(pattern.matcher(m).matches,
m + " is at Gmail", m + " is not at Gmail")
}
}
}
class CustomSpecTest extends JUnit4(CustomMatcherSpec)
Não podemos esquecer de um executor JUnit4 para a integração com a IDE. Seguindo os passos do post passado, você pode ver o resultado das especificações do sistema ToDo e a especificação do Matcher customizado.
BDD é sem dúvida uma metodologia com grande potencial de expansão no mercado e torço pra que muito em breve apareçam oportunidades com tecnologias e metodologias como essas.
Ainda não sei dizer em qual a dimensão de projeto o BDD é mais apropriado, talvez as mesmas dimensões onde TDD e DDD são aplicáveis.
Detalhes como as classes DB, S e todos os fontes do Post, você vai encontrar na aplicação disponibilizada no meu repositório github. Reforço que o projeto froi criado seguindo o tutorial de Getting Started do Lift e modificado para a construção do Post. E não se esqueça de me seguir no Twitter.
1 Comentário
Pular para o formulário de comentários | RSS de comentários [?] | trackback uri [?]