Ir para o conteúdo
ou

Software livre Brasil

Tela cheia
 Feed RSS

Gustavo Dutra - Um pouco de tudo, nada de pouco

27 de Maio de 2009, 0:00 , por Software Livre Brasil - | Ninguém está seguindo este artigo ainda.

Como utilizar arrays e arrays associativos em shell script

12 de Fevereiro de 2011, 0:00, por Software Livre Brasil - 0sem comentários ainda

Olá! Em várias linguagens de programação está presente o array. Em Shell-Script não é diferente. Nas séries 4.x do Bash é possível, além do array indexado, utilizar arrays associativos, ou seja, com uma string como índice.

Os arrays do shell script podem ter apenas uma dimensão, ou seja, apenas um nível de profundidade. Apesar disto, são dinâmicos e podem abrigar quantos valores forem precisos. Além de serem muito úteis em programas maiores que utilizam essa linguagem.

Como sou da filosofia de que um código vale mais que mil palavras, seguem vários exemplos de uso.

leia mais



Sobre as atualizações do site

18 de Janeiro de 2011, 0:00, por Software Livre Brasil - 0sem comentários ainda

Após um longo tempo sem atualizar, cerca de 6 meses, o site foi atualizado com um novo layout. Também foi migrado de Wordpress para Drupal porque com ele eu sinto mais a vontade de criar novas seções, conteúdos e etc, visto o poder do CMS Drupal. Caso alguma URL não esteja funcionando apropriadamente, favor entrar em contato.

Além do disto, foi melhorada a questão das personas que compõe o Gustavo Dutra. Separei melhor o Geek do Poeta e também introduzi um novo, o Podcaster. O podcaster pretende simplesmente expressar suas opiniões sobre a vida, o universo e tudo mais, sem pretensões.

leia mais



Como fazer testes unitários em Models no CakePHP

29 de Julho de 2010, 0:00, por Software Livre Brasil - 0sem comentários ainda

Olá! Dando continuidade ao post anterior Introdução a testes unitários no CakePHP e SimpleTest, este post tenta explicar como testar models.

A camada de modelo (model) geralmente é conhecida pela sua capacidade de abstrair as fontes dos dados, tornando o sistema independente de banco de dados, isto é, independente se é utilizado MySQL, Postgres ou até mesmo arquivos CSV ou XML.

Pelo fato desta camada ser responsável pela manutenção dos dados do sistema, é de extrema importância testá-la. Os testes unitários devem garantir que esta esteja funcionando de acordo, para evitar incosistências.

Vamos criar os testes unitários desde o início, desde a criação das tabelas do banco de dados, passando pelos testes e a criação do model em si.

Vamos criar no banco de dados, que já suponho estar configurado no CakePHP, a tabela com a qual vamos trabalhar. Vamos criar um Model simples para produtos.

CREATE TABLE `products` (
   `id` INT(15) UNSIGNED NOT NULL AUTO_INCREMENT,
   `name` VARCHAR(200) NOT NULL,
   `description` TEXT,
   `price` DOUBLE(10,2) UNSIGNED NOT NULL,
   PRIMARY KEY (`id`)
)
comment = 'Produtos'
engine = InnoDB

Tendo esta tabela no banco, podemos utilizar o próprio CakePHP para gerar automaticamente o fixture e o model. Para isto vamos executar o seguinte:

cd meu_projeto/app
../cake/console/cake bake model Product

Interagindo com o Shell do cake, você pode criar seu model de forma simples e prática. Observe que ele criou 2 arquivos:

app/tests/fixtures/product_fixture.php
app/tests/cases/models/product.test.php

Em app/tests/fixtures/product_fixture.php vamos definir nossos dados de teste, os dados que utilizaremos nos testes. Para isto, basta ajustar a propriedade $records:

   public $fields = array(
      'id' => array(
         'type' => 'integer',
         'null' => false,
         'default' => NULL,
         'length' => 15,
         'key' => 'primary'
      ),
      'name' => array(
         'type' => 'string',
         'null' => false,
         'default' => NULL,
         'length' => 200
      ),
      'description' => array(
         'type' => 'text',
         'null' => true,
         'default' => NULL
      ),
      'price' => array(
         'type' => 'float',
         'null' => false,
         'default' => NULL,
         'length' => '10,2'
      ),
      'indexes' => array(
	     'PRIMARY' => array('column' => 'id', 'unique' => 1)
      ),
      'tableParameters' => array(
         'charset' => 'utf8',
         'collate' => 'utf8_general_ci',
         'engine' => 'InnoDB'
      )
   );
   public $records = Array(
      Array(
         'id' => 1,
         'name' => 'Nome do Produto',
         'description' => 'Descrição longa',
         'price' => 15.23
      ),
      Array(
         'id' => 2,
         'name' => 'Nome do Segundo Produto',
         'description' => 'Descrição super longa',
         'price' => 12
      )
   );

Pronto, nosso fixture se encontra com a estrutura da tabela na propriedade $fields e os dados na propriedade $records.

Em app/tests/cases/models/product.test.php ficarão nossos testes. O CakePHP já escreve algumas coisas no arquivo:

public $fixtures = array('app.product');

É essencial que todos os models relacionados devam ter seus fixtures adicionados a esta propriedade, pois senão ele não é capaz de criar as tabelas com os dados de teste.

public function startTest) {
   $this->Product = ClassRegistry::init('Product');
}

Este método é executado sempre antes de cada teste (de ser executado o método que inicia com test). É interessante ter o objeto sempre “reiniciado” ao se fazer cada teste, pois senão um teste pode influenciar no valor do outro. Por exemplo, se o campo name tivesse um índice unique, poderia ter erro caso dois métodos de teste tivessem o mesmo valor sendo inserido, sendo que não era isto que estava sendo testado.

Para ter certeza que o objeto está sendo reinicializado, eu sugiro adicionar (somente necessário nos testes de 1.2.X):

public function endTest() {
   unset($this->Product);
   ClassRegistry::flush();
}

Assim temos mais garantia de que tudo vai funcionar como o esperado.

O teste que o CakePHP 1.2.X insere automaticamente não deixa de ser importante, porém, na versão 1.3.X do CakePHP este teste não é mais inserido automaticamente.

public function testIsA...() {
   $this->asssertIsA($this->Product, 'Product');
}

Quando o ClassRegistry::init() não acha o arquivo com o model sendo inicializado, porém consegue encontrar uma tabela que satisfaça o nome deste model, o CakePHP cria automaticamente um model com a classe GenericModel. Assim, será possível saber se o CakePHP está encontrando o model na estrutura de diretórios.

Bom, a primeira coisa que quero testar neste model, é garantir que não será possível a inserção de dados vazios. Os campos são obrigatórios e devem ser preenchidos com valores não vazios.

public function testNoPassedData() {
   $data = Array()
   $this->assertFalse($this->Product->save($data));
   $error_fields = array_keys($this->Product->validationErrors);
   // Verifica se o campo deu erro na validação
   $this->assertTrue(in_array('name', $error_fields);
   $this->assertTrue(in_array('description', $error_fields);
   $this->assertTrue(in_array('price', $error_fields);
}
public function testEmptyData() {
   $data = Array(
      'name' => '',
      'description' => '',
      'price' => ''
   )
   $this->assertFalse($this->Product->save($data));
   $error_fields = array_keys($this->Product->validationErrors);
   // Verifica se o campo deu erro na validação
   $this->assertTrue(in_array('name', $error_fields);
   $this->assertTrue(in_array('description', $error_fields);
   $this->assertTrue(in_array('price', $error_fields);
}

Bom, o nome e a descrição podem conter o que quiser, desde que contenham alguma coisa. Então, a princípio não há necessidade de mais testes. Mas e o preço do produto?

Bom, vamos pensar no preço: um preço é um valor numérico acima de 0. Então podemos por os seguintes testes:

public function testValidPriceFormat() {
   $valid_prices = Array(3, 5.04, 124.3, 12000, 0.01, 1.0000);
   $data = Array(
      'name' => 'nome válido',
      'description' => 'descrição válida',
      'price' => null // será substituido
   );
   foreach ($valid_prices as $counter => $price) {
      $data['price'] = $price;
      $data['name'] = $data['name'];
      $this->assertTrue($this->Product->save($data));
   }
}
public function testInvalidPriceFormat() {
   $invalid_prices = Array('', -14,0,'zero','1,00', 0);
   $data = Array(
      'name' => 'nome válido',
      'description' => 'descrição válida',
      'price' => null // será substituido
   );
   foreach ($invalid_prices as $price) {
      $data['price'] = $price;
      $this->assertFalse($this->Product->save($data));
      $error_fields = array_keys($this->Product->validationErrors);
      // verifica se está no array de erros
      $this->assertTrue(in_array('price', $error_fields));
   }
}

Vejam que não vale a pena misturar as coisas: quando eu testo valores válidos ou inválidos de preço, somente o preço eu modifico. O restante das informações permancem imutáveis e devem ser válidas. O que queremos testar agora é a validação do preço e não de outras partes.

Uma outra coisa importante é considerar strings e valores com vírgulas para estes casos, são potenciais problemas! Aqui vai uma dica: sempre que trabalhar com campos inteiros, verifique o comportamento do sistema com valores 0 e negativos também!

Bom, temos nossos testes. Precisamos fazer eles passarem. Para isto, vamos adicionar o array de validação ao model.

No arquivo app/tests/cases/model/product.test.php vamos por o seguinte:

public $validate = Array(
   'name' => Array(
      'required' => Array(
         'rule' => '/\S/',
         'message' => 'Deve ser especificado um nome para o produto',
         'required' => true
      )
   ),
   'description' => Array(
      'required' => Array(
         'rule' => '/\S/',
         'message' => 'Deve ser especificado uma descrição para o produto',
         'required' => true
      )
   ),
   'price' => Array(
      'valid' => Array(
         'rule' => array('comparison', '>', 0),
         'message' => 'O preço deve ser maior que zero'
      ),
      'format' => Array(
         'rule' => array('numeric'),
         'message' => 'O preço deve ser um valor numérico válido'
      ),
      'required' => Array(
         'rule' => '/\S/',
         'message' => 'Deve ser especificado um preço para o produto',
         'required' => true
      )
   )
);

Bom, agora é só rodar os testes e ver a barra verde. Simples, huh?



Introdução a testes unitários com CakePHP e SimpleTest

25 de Junho de 2010, 0:00, por Software Livre Brasil - 0sem comentários ainda

Há tempos eu venho lendo sobre XP (eXtreme Programming), Scrum e Feature Driven Development (FDD). São metodologias ágeis de desenvolvimento de software. O engraçado é que praticamente, ao meu ver, fazem a mesma coisa, porém de forma diferente. E todas elas frizam a importância dos testes unitários e, mais especificadamente, testes unitários antes do desenvolvimento, o chamado TDD (Test Driven Development). Como nunca havia trabalhado desta forma, ainda não tinha uma opinião sobre isso. Apenas comentários e depoimentos de que usa. Somado a isso, veio aquele senso de desafio, de inovação e curiosidade.

Faz alguns meses, e decidimos, eu e o Emanuel Zabka, implantar o TDD no desenvolvimento na empresa em que trabalhamos. Academicamente é falado muito em refactoring e mudanças de requisitos, mas na prática, eu nunca havia notado a quantidade de modificações que um mesmo código/classe/arquivo é modificado num curto período de tempo e a longo prazo.

Os testes me ajudaram a enxergar, não só as dependências do sistema, como também, acoplamentos desnecessários; a qualidade dos códigos; do design do projeto; e, consequentemente, diminuiu muito os bugs mais corriqueiros, como validação de campos. Mesmo tendo várias coisas boas, há coisas ruins, como aumento do tempo de desenvolvimento – o que, apesar de nunca ter medido, diminui o retrabalho e tempo utilizado no futuro em correções de bugs -, aumento da complexidade da arquitetura – a fim de manter uma maior coesão com baixo acoplamento, entre outros menos relevantes.

Unit Test

The basic concept of unit testing is write more code which will test the main code we’ve written, by “throwing” sample data at it and examining what it gets back.

Harry Fuecks

Enfim, uma boa definição de testes unitários é esta supracitada. São escritos mais códigos para testar o código principal, utilizando dados de testes para analisar o comportamento e o retorno do código sendo testado.

CakePHP

  • Framework de desenvolvimento rápido pra Web com PHP
  • Utiliza padrões MVC e ORM
  • Extensível para desenvolvimento, manutenção e deploying.
  • OpenSource / Comunidade muito ativa e amigável
  • Orientada a Objetos
  • Mais detalhes sobre o CakePHP

SimpleTest

  • Praticamente mesma coisa que JUnit, JMock e PHPUnit.
  • Guiado através de asserções
  • Possibilidade navegação em requisições HTTP para testar interface gerada (HTML)
  • OpenSource
  • Orientada a Objetos
  • Mais detalhes sobre o SimpleTest

CakePHP e SimpleTest

  • Startup (criação do arquivo, casos de testes básicos) dos testes unitários automaticamente
  • Customizações no template de Report (Web com template padrão)
  • Rápida execução de testes individualizados
  • Rápida execução de grupos de testes (execução de vários testes unitários sequencialmente)
  • Pode-se usar Mock para objetos internos do CakePHP ou da aplicação
  • Pode-se testar aplicação toda (APP + Plugins)
  • Pode-se testar somente a aplicação (APP)
  • Pode-se testar plugins separadamente
  • Pode-se testar em modo batch
  • Integração dos dois frameworks

Convenções e Padrões

Para o SimpleTest não há padronização no nome dos arquivos, mas o CakePHP estabelece um a partir da convenção das classes utilizados por ele. Ex: respectivamente as classes de controller e model de um Post:

PostController
tests/cases/controllers/post_controller.test.php
Post
tests/cases/models/post.test.php
  • O nome da classe contida no arquivo é derivado a partir do nome do mesmo.
  • Esta classe deve extender a classe CakeTestCase ou CakeWebTestCase
  • A assinatura dos métodos que contém asserções (métodos de teste) devem iniciar com “test”, como em testPostControllerAddPost().
  • Métodos que são executados como eventos:
    start()
    Primeiro método chamado. Executado quando se iniciam os testes
    end()
    Último método chamado. Executado quando se concluí todos os casos testes
    startCase()
    Executado antes do caso de teste começar a ser testado (lembrando que um teste pode ser composto de um ou mais casos de teste)
    endCase()
    Executado depois que o caso de teste foi testado por completo
    before($method)
    Anuncia o início de um método de teste
    after($method)
    Anuncia o término de um método de teste
    startTest($method)
    Executado antes de um caso de teste iniciar
    endTest($method)
    Executado depois que um caso de teste termina

Mais detalhes sobre as convenções

Fixtures

Fixtures são uma forma de utilizar dados de teste. São matrizes de dados que correspondem à registros em um banco de dados.
Cada Model possui uma Fixture, que irá não só ter os dados como a representação dos campos que compõe a tabela do banco, pois a partir destes dados o framework irá:

  1. Criar uma tabela prefixada (por padrão) de “test_suite_” no banco de dados para cada fixture necessário (ou seja, cada model utilizado).
  2. Inserir os dados de teste na tabela de teste
  3. Executar os testes em cima destes registros
  4. Remover os dados da tabela de teste
  5. Remover a tabela no banco de dados.

Ou seja, assim é garantido que os dados serão sempre os mesmos e que não comprometerá a base de dados atual com sujeira, podendo-se executar
estes testes a qualquer momento atrás de problemas. Testes que envolvem banco de dados com índices únicos, chaves primárias ou outras constraints
estão livres de erros (erros não propositais) durante os testes.

Veja mais sobre Fixtures

Asserções

  • assertTrue($x)
  • assertFalse($x)
  • assertNull($x)
  • assertEqual($x, $y)
  • assertIsA($object, ‘ClassName’)
  • assertIdentical($x, $y)
  • assertPattern(’/REGEX/’, $x)
  • expectError();

Se você se interessou, para mais detalhes sobre os testes unitários no CakePHP, recomendo a leitura deste post da Debuggable: Unit Testing in CakePHP Part 1 – Introduction to Unit Testing

Agradecimentos especiais a André Leitzke e Viviane de Souza por terem me ajudado com a caracterização e descrição do framework!



Acessando a freenode.org por SSL no Irssi

31 de Janeiro de 2010, 0:00, por Software Livre Brasil - 0sem comentários ainda

Bom, recentemente a FreeNode.org adicionou suporte a conexões SSL. Não que eu faça questão, porém meu irssi perdia a conexão com o server. Fui para o google e decidi postar a dica de como resolvi o problema e habilitei a conexão SSL.

Primeiro, eu baixei um script que a própria FreeNode.org disponibiliza para o irssi, o instalei e depois algumas dependências.

cd ~/.irssi/scripts/autorun #Se o diretório não existir, crie-o
wget http://www.freenode.net/sasl/cap_sasl.pl

Como maioria das dependências não estavam nos repositórios oficiais do Arch Linux nem do AUR, eu tive que as instalar separadamente e ai já aproveitei pra fazer um script de instalação com todas as dependências. Esse script funciona em qualquer distro. São necessários os seguintes módulos: Math::BigInt, Math::BigInt::FastCalc, Math::BigInt::GMP, Math::BigInt::Pari, Crypt::DH, Crypt::Blowfish e Crypt::OpenSSL::BigNum.

Abra um novo arquivo e cole as linhas abaixo, será nosso script de instalação.

#!/bin/bash
 
packages=(Math-BigInt-1.89 Math-BigInt-FastCalc-0.19 Math-BigInt-GMP-1.24 Math-BigInt-Pari-1.13 Crypt-DH-0.06 Crypt-OpenSSL-Bignum-0.04)
 
mkdir /tmp/cpan-comp
cd /tmp/cpan-comp
wget http://www.cpan.org/authors/id/T/TE/TELS/math/Math-BigInt-1.89.tar.gz
wget http://www.cpan.org/authors/id/T/TE/TELS/math/Math-BigInt-FastCalc-0.19.tar.gz
wget http://www.cpan.org/authors/id/T/TE/TELS/math/Math-BigInt-GMP-1.24.tar.gz
wget http://www.cpan.org/authors/id/T/TE/TELS/math/Math-BigInt-Pari-1.13.tar.gz
wget http://www.cpan.org/authors/id/B/BT/BTROTT/Crypt-DH-0.06.tar.gz
wget http://www.cpan.org/authors/id/I/IR/IROBERTS/Crypt-OpenSSL-Bignum-0.04.tar.gz
 
for pkg in $packages; do
	tar -xzvf "$pkg.tar.gz" ;
	cd "$pkg" ;
	perl Makefile.PL ;
	make && make test && sudo make install ;
	cd .. ;
done;

Inicie o irssi e digite:

/sasl set freenode Seu_nick Sua_Senha BH-BLOWFISH
/sasl save
/save
/quit

Agora, é só ajeitar o config do irssi.

vim ~/.irssi/config
servers = {
	{
		address = "irc.freenode.org";
		chatnet = "freenode";
		port = "7000";
		use_ssl = "yes";
		ssl_verify = "yes";
		ssl_capath = "/etc/ssl/certs";
	}
}

Entre no irssi novamente e pronto! Só usar o /connect freenode !

E ai, gostou?