Codemountain, Paulo Suzart's Blog

Especificando validações com Scala, Specs e Lift – Parte II

with 3 comments

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.

Written by paulosuzart

outubro 20, 2009 às 10:55 am

Publicado em bdd, lift, scala

Tagged with , , ,

3 Respostas

Subscribe to comments with RSS.

  1. Hi Paul,

    Thanks for your posts on specs and lift, they are very valuable!

    I just want to say that the easiest way to run specifications as JUnit is to use the SpecificationWithJUnit class as specified here: http://code.google.com/p/specs/wiki/RunningSpecs#Run_your_specification_with_JUnit4, and not to use a JUnit4 runner which was used in Lift examples before (if that’s what you recommend from what I understand of the translation of your post by Google translate :-)).

    This way the specifications are directly runnable with Maven.

    Eric

    Eric

    setembro 13, 2010 at 7:00 pm

    • Thanks for the tips! I was sort of beginning (and still am) with Specs. I’m trying a lot to be paid for code scala. Still hard to happen here.

      paulosuzart

      setembro 13, 2010 at 8:19 pm


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: