Estamos contratando testers para o nosso time de engenharia na Microsoft.

GFM Latam é o time responsável pelo desenvolvimento/localização de funcionalidades locais para paises da América Latina, para o MS Dynamics AX. Somos responsáveis em tornar o AX um produto competitivo no mercado nacional e isto é uma tarefa e tanto.

Ser um SDET (Software Development Engineer in Test) na Microsoft é bem diferente do que muitos percebem a atividade de testar software. Um dia-a-dia movimentado, cheio de desafios técnicos e funcionais. Neste post falo mais sobre isto.

Se você está em busca de um desafio e se enquadra na descrição da vaga, mande seu currículo!

Interessante a conversa do Hanselman com o Chris Sell. Dois caras bastante técnicos tornando-se leads.

Atentem para a frase do Hanselman “Vc não ganha um aumento, nada. Só um lead no final do nome, um monte de cursos online e acesso a um monte de ferramentas …”

http://channel9.msdn.com/posts/Glucose/Hanselminutes-on-9-Follow-up-6-months-later-with-Chris-Sells-on-Managing-People-and-Your-Time/

Acabei de ler um post curto do Joel, com quem trabalho, com certeza vou ler o artigo indicado.

Já tive duras discussões em grupos de desenvolvedores e arquitetos sobre testes. Sempre que escuto que um desenvolvedor senior não precisa tanto assim de testes a minha resposta é: A minha evolução como desenvolvedor para alcançar a senioridade foi aceitar que eu não sou capaz acertar um algoritmo de 20 linhas sem criar bugs. Ou seja, ser humilde o suficiente para rever e testar o seu próprio código diversas vezes antes de levantar o braço e falar que está pronto.

Se eu já tinha dificuldade de explicar o que eu fazia como engenheiro de software, agora trabalhando na área de testes a coisa ficou ainda pior. Vou tentar simplificar isto descrendo um dia típico de um engenheiro de testes, doravante chamado SDET, na Microsoft.

As atividades são bem diversas e dificilmente trabalhamos com apenas uma funcionlidade e sempre algum fantasma do passado vem nos assombrar em forma de bugs de releases passados. Portanto, ser multitask é uma característica básica de um SDET.

O dia de João começa com os resultados dos testes executados no final de semana. Os testes, de uma de suas funcionalidades, que falharam são analisados. João encontra um defeito e o descreve no item tracking (TFS ou algo similar) com um repro step detalhado e o cenário que este bug acontece e o impacto que ele causa, o que facilita depois a priorização do mesmo. Dois testes falharam por erros no próprio teste, um test issue, que João também adiciona ao item tracking para que sejam consertados o mais rápido possível.

Ainda antes do almoço João vai a uma reunião sobre uma nova funcionalidade. Após ter lido a especificação, ele discute com o time suas dúvidas, sugestões e preocupações sobre como e o que testar para garantir a qualidade da nova funcionalidade.

De volta do almoço João escreve a primeira versão da especificação de testes para esta nova funcionalidade. Neste documento ele descreve as estratégias de teste a serem utilizadas, os testes que serão realizados e suas prioridades. Além de ser um momento de reflexão sobre o que e como testar, este documento servirá para que colegas de time, desenvolvedores, PMs e outros testers, revisem suas idéias e sugiram melhorias e proponham alternativas.

No meio desta atividade João é interrompido pelo email de um colega pedindo sua ajuda em uma ferramenta de testes. João conhece muito bem esta ferramenta e ajuda regularmente seus colegas, ele acabou virando uma referência no time quando o assunto é esta ferramenta.

A terça-feira começou mal, um defeito de alta prioridade chegou ao time de sustained engineering e eles precisam da ajuda de João para testar o conserto, devido o seu conhecimento da funcionalidade. João prepara o ambiente necessário, reproduz o problema, aplica o conserto. Após alguns testes manuais ele tem certeza de que o conserto é bom, implementa um testcase que reproduz o problema e conserta outros três testcases que foram impactados pelo conserto.

A tarde João dedica ao desenvolvimento de testes para uma outra funcionalidade. Tanto automatizando a execução de testes via UI como acessando diretamente a camada de negócios. Em um certo momento, João identifica que a API funcional, que abstrai detalhes da API de testes em uma linguagem mais própria do negócio, não tem a função que ele precisa. Ele implementa esta nova função e avisa ao time. No final do dia, João envia os testcases prontos para revisão e aguarda a resposta no dia seguinte para colocar este código no controle de versão.

Acabei precisando de dois dias... E eles ficaram bem cheios. Lógico que na verdade o dia-a-dia não é tão movimentado assim, mas eu quis incluir todas as atividades que um SDET normalmente pode executar.

No final do ano passado aceitei uma nova posição no meu time. A partir de então estou atuando com Test lead do time GFM Latam. Este time é responsável pela localização do MS Dynamics AX para o Brasil e América Latina. Já fazia parte deste time há alguns anos, mas atuando como engenheiro de software.

Para quem está se perguntando “porque o maluco saiu de desenvolvimento para testes?” sugiro conhecer um pouco mais da importância da displicina de testes dentro da Microsoft. O investimento na displicina assim como a importância dela no processo de desenvolvimento e muito grande.

Para quem está concluindo que “o cara virou gerente para poder subir na carreira” pode esquecer. A carreira em Y aqui é uma realidade e o meu objetivo de crescimento a médio prazo continua exatamente o mesmo. Ou seja, tanto como engenheiro, quanto como lead eu conseguiria chegar no mesmo lugar.

O que me motivou foi realmente a oportunidade de ter novos desafios, tanto tecnicamente, me aprofundando na complexa atividade de testar software, quanto em liderança, a partir de agora sou responsável não pela minha produtividade, mas sim pela produtividade de um time, e também pela carreira dos colegas que integram este time.

Portanto a partir de agora, além das baboseiras ocasionais sobre engenharia de software e processos, escrevei também sobre novos temas como teste de software e liderança em times de engenharia.

Não se iludam com o desenho acima, este macaco tem mais QI que a maioria dos desenvolvedores que conheci, inclusive eu, é metódico, sem compaixão e bem mais forte que você. Portanto, pense duas vezes antes de liberar algum pedaço de código sem garantir que ele funciona!

Uma das minhas partipações no Teched deste ano foi uma pequena participação na palestra do colega de time, Cezar Guimarães: “Construindo uma aplicação com silverlight 3”. Espero que os ouvintes tenham gostado.

Minha tarefa, além de aprender alguma coisa de silverlight, era mostrar algumas ferramentas que possibilitam desenvolvedores automatizarem seus testes para silverlight.

Apresentei o Silverlight Unit Test Framework, criado por Jeff Cox e utilizado pelo time do Silverlight Toolkit, para automatizar seus testes.

Como referência sugiro o blog do Jeff Cox e o projeto Silverlight Toolkit no codeplex. Neste projeto você encontrará o código-fonte do framework e de todos os testes unitários escritos pelo time utilizando o framework.

Caso seja necessário utilizar Mocks ou Stubs no seu teste, já existe uma versão compilada para Silverlight do Moq.

O código desenvolvido e apresentado na palestra encontra-se no blog o Cezar: teched 2009 – código fonte de “construindo uma aplicação com silverlight 3”.

< Notícias >

Nesta sexta-feira os estagiários do nosso time fizeram apresentações sobre os princípios SOLID para o time. O D acabou “sobrando” para mim, pois são apenas quatro estagiários. Eu fiz uma apresentação bem simples, quase simplista. Mas como o material já estava pronto, decidi colocar aqui no blog.

Disclaimer: Este código é um simples exemplo cuja única motivação é apresentar o princípio em questão. Tentar utilizá-lo pode causar dor de cabeça, impotência sexual e outras complicações.

Dependency Inversion Principle

Vamos considerar um exemplo simples de aplicação que copia mensagens do console e as escreve novamente no console.

1

No dia seguinte é necessário adicionar uma nova funcionalidade, a opção de copiar para o Clipboard ao invés do Console. Posso utilizar uma enumeração como um discriminador do output desejado.

2

Para isto preciso alterar a função que implementa minhas regras de negócio (vamos fingir que existe algum valor em copiar mensagens). O motivo é que minha classe, que está em nível de abstração mais alto depende diretamente de uma classe de um nível mais baixo de abstração.

3

O “Dependency inversion principle” nos diz que:

4

Ou seja, módulos de alto nível não devem depender de módulo de baixo nível, mas sim de abstrações, algo como:

 

 

 

5

 

Então para aplicar o princípio em meu exemplo, seria necessário abstrair as atividades de mais baixo nível.

Abstraindo a atividade de escrever a mensagem

Utilizando uma interface abstraio a função de escrever a mensagem. Meu módulo de alto nível não sabe mais como imprimir a mensagem no console, conhece apenas a abstração.

6

Para atender os requisitos, implemento dois writers concretos:

7

 

Agora abstraio a função de ler, que também está ligada a módulos de níveis mais baixos.

8 

O código cliente, o que utiliza a minha API, fica deste jeito:

 

 

 

9

 

Repare que minha solução aumenta a complexidade neste código, agora o cliente precisa saber qual “escritor” e qual “leitor” utilizar. É um trade-off que você deve decidir se vale a pena, ou não.

Design é tentar maximizar as escolhas e os trade-offs. Dificilmente você irá encontrar decisões sem pontos negativos. Se você não consegue encontrá-los, procure melhor, eles devem estar escondidos em algum lugar.

Veja a notícia abaixo e mande seu currículo.

Microsoft Brasil abre vagas para núcleo de desenvolvimento de software Dynamics em SP

A Microsoft Brasil amplia sua área de desenvolvimento local de software Dynamics, localizada em São Paulo, e contrata mais engenheiros de software. Os pré-requisitos para concorrer às vagas incluem conhecimento em linguagem de programação orientada a objetos (C#, C++, .Net, Java) e criação de códigos; graduação em Ciências da Computação, Engenharia ou Tecnologia da Informação; excelente capacidade de comunicação; e inglês fluente. A experiência no desenvolvimento de aplicações de negócios pode ser uma vantagem no processo de seleção.

Os profissionais criarão novas funcionalidades das soluções de gestão empresarial e do relacionamento com o cliente, Dynamics ERP e Dynamics CRM, respectivamente. O centro de desenvolvimento de software foi inaugurado em 2006 e agora, com as novas contratações, dobrará de tamanho.  A divisão Microsoft Business Solutions, responsável pela linha de produtos Dynamics, é a que mais cresce em todo o mundo, sendo que registrou 140% de aumento no volume de negócios no último ano fiscal, encerrado em junho de 2008.

Para se candidatar, os interessados devem acessar: http://ms.peoplehr.com.br.

< Notícias >

A série de artigos sobre high-order functions e Linq gerou algumas conversas sobre as lambda expressions que utilizei. Ali, de bate pronto, tive dificuldades de explicar o conceito e percebi que ele não é tão trivial tanto para explicar, como para entender.

Então vou tentar apresentar de uma forma alternativa, mostrando a evolução da linguagem em direção às lambdas expressions.

No .Net framework 1.1 os delegates já existiam. Sua utilização prática, no entanto, era restrita aos eventos. Não que isto fosse uma restrição do framework, era mais uma questão de pouco conhecimento dos desenvolvedores mesmo.

Como expliquei no Brincando com C#, os delegates permitem que uma função seja passada como parâmetro ou retorno de outra função. Desta forma posso delegar parte do meu algoritmo para outra função.

Por exemplo, se tenho um método que transforma linhas de texto. Por algum motivo a regra de transformação pode variar. Para não precisar reescrever o método inteiro para cada situação, utilizo um delegate para isolar este trecho de código e poder reaproveitar todo o resto.

O passo-a-passo que a gente já conhece: Declara um delegate, cria um método que recebe o delegate como parâmetro, cria o método que “implementa” o delegate.

    public delegate string Parser(string line);

    public IEnumerable<string> ParseText(IEnumerable<string> text, Parser parser)
    {
        var lines = new List<string>();

        foreach (string line in text)
        {
           lines.Add(parser(line));
        }

        return lines;
    }

    private string RemoveSpaces(string line)
    {
        return line.Replace(" ", "");
    }

Utilização é simples, chama o Parser, mandando como parâmetro o RemoveSpaces.

    var parsedLines =  (List<string>) ParseText(lines, RemoveSpaces);

Anonymous methods

O .Net Framework 2.0 trouxe a novidade dos anonymous methods. O nome não podia ser mais específico, métodos anônimos são ... anônimos! Ou seja, sem nome. Não é necessário definir formalmente o método, posso simplesmente declará-lo localmente.

    var parsedLines = (List<string>) ParseText(lines, 
                                               delegate(string line)
                                               {
                                                   return line.Replace(" ", "");
                                               });

Sinceramente, para um método que deveria ser anônimo, ele é um método bem declarado e tem uma sintaxe verborrágica.

Vamos fazer um exercício para imaginar uma sintaxe um pouco mais simples, retirando tudo o que desnecessário nesta declaração. Na Figura marquei o que é inútil nesta declaração. A começar por definir o método como um delegate, já que a chamada do método espera um delegate isto é totalmente desnecessário. Tipificar o(s) parâmetro(s) também é desnecessário, pois a assinatura do método já foi declarada no delegate. Por último, o parêntesis que determina o início e o fim do método.

anonMethod

 

 

 

 

Vemos que seria possível definir uma sintaxe ainda mais simples do que os métodos anônimos. E é exatamente isto que foi feito para o .Net Framework 3.0, com as Lambdas expressions.

 

Lambda expressions

O .Net Framework 3.0 trouxe esta nova sintaxe para escrever métodos anônimos, mais concisa e mais legível. O exemplo canônico, e mais simples, das expressões lambdas, é o calculo de uma raiz quadrada:

lambda

 

 

A Figura mostra que à esquerda são declarados os parâmetros da expressão, à direita a expressão em si. Neste simples exemplo é definido o parâmetro de entrada x e a expressão é a multiplicação do parâmetro por ele mesmo, ou seja, a sua raiz quadrada.

    var parsedLines = (List<string>) ParseText( lines, line => line.Replace( " ", "" ) );

Nosso exemplo, utilizando uma expressão lambda no lugar do método anônimo, fica bem mais simples e limpo. Uma coisa que pode confundir é como o compilador sabe qual o tipo do parâmetro line? Elementar, a função espera um delegate, cuja assinatura recebe um parâmetro do tipo string, por inferência, o compilador percebe que o parâmetro é do tipo string. A IDE utiliza a mesma regra para oferecer o intellisense, como mostra a Figura.

intellisense

 

 

 

 

 

 

A sintaxe lambda é bem mais robusta e complexa do que mostram estes dois exemplos que fiz aqui. Ela aceita parâmetros com tipo definido, métodos sem parâmetros, entre outras coisas. Um guia mais completo da sintaxe pode ser encontrado aqui.

Espero ter ajudado um pouco na compreensão deste novo conceito do .Net Framework.

< Exemplos >

Outro dia sai do trabalho com uma cópia de um artigo do Alistair Cockburn, Hexagonal architecture, que fala sobre arquitetura de aplicações. Acabei em uma mesa de bar e o artigo incitou um debate sobre a necessidade de desenvolver aplicações que são agnósticas em relação ao Banco de dados, em outras palavras, aplicações que funcionam com diversos bancos de dados somente com alteração de configurações.

Para alguns na mesa isto é desnecessário, já que este não é um requisito real na grande maioria dos casos. São raros os exemplos de aplicações que foram migradas de um SGBD para outro e não é possível prever que isto vá acontecer um dia. Portanto, adicionar esta característica a aplicação seria adicionar complexidade desnecessariamente.

O problema não são os argumentos, mas sim o assunto. Ele é tão polêmico, quanto inútil. Pior que isto, nos distrai do que é realmente importante, como aconteceu nesta conversa que tive.

Eis a introdução do artigo, que iniciou todo o debate:

Create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement.

O negrito foi colocado propositalmente para destacar o assunto mais importante, que é ofuscado pela inútil discussão sobre banco de dados. Os principais requisitos da divisão de responsabilidades entre camadas e artefatos de uma aplicação são: Testabilidade e manutenibilidade.

A lógica de comunicação com o Banco de dados é complexa o suficiente para ser considerada uma responsabilidade que deve ser isolada e testada à parte. Mas isto já é feito pela camada de acesso a dado na velha conhecida arquitetura em três camadas.

Se eu consigo isolar e substituir esta camada por test double para poder testar as outras camadas sem precisar acessar o banco de dados meus já estão infinitamente melhores.

É possível testar uma aplicação sem isolá-la em pedaços menores. Na verdade isto é até desejável, são conhecidos como testes de aceitação, que testam cenários fim a fim, inclusive a persistência e leitura de dados no banco de dados. Todavia estes testes normalmente são pouco assertivos e consomem bastante tempo.

Um teste destes pode falhar por que: A UI não está correta, a regra de negócios está com defeito, o código de persistência está incorreto, a conexão de rede falhou, o tempo de resposta expirou, o dado no Banco de dados está incorreto, o Banco de dados está offline ou <preencha aqui o seu motivo favorito>. Ou seja, por qualquer motivo.

Além disto, a comunicação aplicação SGBD ainda é um pontos que mais consome tempo de execução, o que torna estes testes demorados.

Em suma, é desejável, senão necessário, conseguir testar minha aplicação sem precisar acessar o banco de dados a cada teste.

Mas se é possível substituir toda a camada de acesso a dados por uma falsa, ou um test double, então eu consigo substituir por outra que acesse outro repositório de dados. Então a propriedade “banco de dados agnóstica” vem de graça, ou a um custo muito baixo, como resultado da correta divisão de responsabilidades e do desacoplamento das camadas.

Agora como todas as decisões feitas na construção de um software, custos e benefícios devem ser avaliados. Faz algum tempo que eu acredito que testabilidade não é uma característica negociável, pois o custo de não tê-la é enorme e, mais dia menos dia, virá morder o seu calcanhar.