Mostrando postagens com marcador VisualStudio. Mostrar todas as postagens
Mostrando postagens com marcador VisualStudio. Mostrar todas as postagens

terça-feira, 24 de maio de 2011

Intellisense do SQL Server 2008 R2 e o SP1 do Visual Studio 2010

Bom dia pessoal.
Nesse fim de semana formatei a minha máquina e aproveitei para deixar o SQL Server 2008 R2 como instância default, atualizar o VS 2010 com o SP1 e finalmente instalar o Win7 SP1.

Quando fui trabalhar na segunda-feira descobri que o intellisense do SQL Server Management Studio não estava funcionando. Fiz o troubleshooting básico de configuração do SSMS e nada! Pesquisando um pouquinho acabei por descobrir o culpado disso tudo: o Service Pack 1 do Visual Studio 2010!

A solução dada pela Microsoft é instalar o Cumulative Update 7 do SQL Server 2008 R2 para solucionar o problema. Eu instalei e tudo voltou a funcionar normalmente.

Algumas referências para o acontecido:
FIX: The IntelliSense feature in SSMS 2008 R2 may stop working after you install Visual Studio 2010 SP1
Cumulative Update package 7 for SQL Server 2008 R2

E um KB legal de acompanhar: The SQL Server 2008 R2 builds that were released after SQL Server 2008 R2 was released 

Eu vi que algumas pessoas tiveram um problema com projetos do DBPro depois de instalado do CU#7, mas não aconteceu comigo.

[]s
Luciano Caixeta Moreira - {Luti}
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm
http://www.srnimbus.com.br/

terça-feira, 27 de outubro de 2009

Testes de unidade em aplicações multi-thread

No meu último artigo eu trouxe uma solução para um problema que eu passei aqui na Sr. Nimbus, já esse post não trás boas novidades não, apenas mostra uma característica do Visual Studio 2010 (e acredito que anteriores) de uma história com testes de unidade e aplicações multi-thread.

Vamos ao nosso cenário básico: imagine uma classe responsável por disparar alguns workers em background de acordo com a tarefa que cada um deve executar. Esses procedimentos por si geram notificações através de eventos que podem ser tratados por outras classes da sua solução. Nada demais, o código para isso (bem simplificado) poderia ser assim:

public struct InfoTarefa
{
public string Info;
public DateTime horaExecução;
}

public class NegocioTeste
{
public void ExecutaTarefa(object informação)
{
InfoTarefa tarefa;
tarefa.Info = (string)informação;
tarefa.horaExecução = DateTime.Now;

if (TarefaCompleta != null)
TarefaCompleta(tarefa);
}

public delegate void NotificarTarefaCompleta(InfoTarefa tarefa);

public event NotificarTarefaCompleta TarefaCompleta;
}


public class ThreadEmBackground
{
public List tarefas;
private NegocioTeste negocio;

public ThreadEmBackground(NegocioTeste n)
{
negocio = n;
}

public void ProcessaTarefas()
{
foreach (string tarefa in tarefas)
{
Thread t = new Thread(negocio.ExecutaTarefa);
t.Start(tarefa);
t.Join();
}
}
}
Por simplicidade deste exemplo eu faço um Join() da thread criada, esperando o fim da execução, para deixar esse exemplo bem controlado, mas na aplicação de verdade isso é diferente.
Para testar o código acima basta fazermos o setup correto das pré-condições, colocando as verificações do resultado na rotina de tratamento do evento TarefaCompleta.

[TestMethod]
public void TesteBásico()
{
//SETUP
List tarefas = new List();
tarefas.Add("Tarefa 01");
NegocioTeste negocio = new NegocioTeste();
ThreadEmBackground gerenteThreads = new ThreadEmBackground(negocio);
gerenteThreads.tarefas = tarefas;
negocio.TarefaCompleta += RecebeInfoTarefa;

Assert.AreEqual(1, gerenteThreads.tarefas.Count);

// Execução do curso básico
gerenteThreads.ProcessaTarefas();
}

public void RecebeInfoTarefa(TestesMultiThread.InfoTarefa tarefa)
{
Assert.AreEqual("Tarefa 01", tarefa.Info);
Assert.IsTrue(tarefa.horaExecução > DateTime.Now.AddMinutes(-1));
}
Mas se a verificação falhasse no tratamento do nosso evento, que acontece em outra thread, olhe na figura abaixo o que veríamos:



Um Error aparece no lugar do Failed, com um ícone de warning (?!) e a mensagem “The agent process was stopped while the test was running”. Se clicarmos duas vezes em cima do Error para analisar o que aconteceu (ação já automática com a falha usual do assert), a mesma mensagem será exibida, o que não ajuda em nada (no caso do Failed vemos a stack trace).

Aí você descobre que deve clicar em “Test run error” para ver a stack trace, onde conseguirá a informação “One of the background threads threw exception: Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: Assert.Fail failed. Problema na validação”.

Isto é, por se tratar de uma thread em background, a maneira como o Visual Studio trata as verificações é diferente do comportamento usual, o que eu achei ruim. Isso mata alguém, não vejo maiores problemas, mas fico imaginando se trará algum impacto durante a automação dos testes ou em momentos diferentes, o que me deixa um pouco inseguro.

Outra coisa que eu tentei através do Visual Studio e não consegui ver uma maneira fácil (a não ser codificar), foi a possibilidade de executar o testes orientando o VS para disparar várias threads de uma só vez, me ajudando a testar condições de corrida.

Para ser super sincero, pode ser que o Visual Studio tenha recursos nessa área de testes para aplicações multi-thread, mas em algumas pesquisas rápidas que fiz não encontrei muitas referências e um funcionário da MS diz no fórum que “Nós não temos uma ótima história para contar sobre aplicações multi-threaded”. Em: http://social.msdn.microsoft.com/Forums/en-US/vststest/thread/c19e9ba8-52db-4970-99a4-04468206baf6/
Ainda vou explorar o DevCenter de computação paralela (http://msdn.microsoft.com/en-us/concurrency/bb895950.aspx) e o único recurso que eu achei para esse cenário, o projeto CHESS (http://research.microsoft.com/en-us/projects/chess/).

Para fechar o artigo: Com os computadores pessoais entrando na era dos multi-cores e todo mundo falando da necessidade de termos aplicações que aproveitem de verdade esses recursos (e até a GPU), espero que o Visual Studio traga boas novas nessa área e ajude os desenvolvedores, o que acredito que acontecerá.

Se você tiver alguma dica ou experiência para compartilhar, estou ansioso para ouvi-lo!
Baixe o PDF e código fonte aqui.



[]s
Luciano Caixeta Moreira - {Luti}
Chief Innovation Officer
Sr. Nimbus Serviços em Tecnologia Ltda
luciano.moreira@srnimbus.com.br
www.twitter.com/luticm

segunda-feira, 26 de outubro de 2009

Testes de unidade com deployment de arquivos


Olá pessoal, vamos de Visual Studio hoje.


Imagine um cenário bem comum: Estamos desenvolvendo um projeto que manipula arquivos e, para ajudar o desenvolvimento, você cria uma classe utilitária que lê do arquivo de configuração qual é o repositório local (diretório) onde os arquivos são armazenados. Essa classe auxiliar será utilizada por toda aplicação, então não existirá somente para testes. Exemplo de código abaixo:


public class Configuração
{
private static string repositórioArquivos;

static Configuração() {
repositórioArquivos = ConfigurationManager.AppSettings["RepositórioArquivos"];
}

public static string RepositórioArquivos
{
get { return repositórioArquivos; }
}
}

public void RenomearArquivo(string nomeArquivo, string novoNomeArquivo)
{
string repositório = Configuração.RepositórioArquivos;
File.Move(Path.Combine(repositório, nomeArquivo), Path.Combine(repositório,
novoNomeArquivo));
}

Antes ou depois de codificar a classe, você desenvolve alguns testes para testar seu componente, então para tudo funcionar corretamente você adiciona ao seu projeto de testes um arquivo App.Config e colocar uma entrada no AppSettings, que será lida pelo auxiliar de configuração:

[TestMethod]
public void TesteCenárioUsoBásico() {

// Pré-condições
Assert.IsTrue(File.Exists(Path.Combine(
TestesComArquivos.Configuração.RepositórioArquivos, "arquivoqualquer.txt")));

TestesComArquivos.ClasseNegocio negócio = new TestesComArquivos.ClasseNegocio();
negócio.RenomearArquivo("arquivoqualquer.txt", "novoarquivotexto.txt");

// Pós-condições
Assert.IsTrue(File.Exists(Path.Combine(
TestesComArquivos.Configuração.RepositórioArquivos, "novoarquivotexto.txt")));
}


Execute o projeto (em anexo a este artigo) e tudo funcionará perfeitamente. Quer dizer, mais ou menos, pois aqui temos alguns problemas:

1 - Depois que você executar esse teste, o arquivo estará renomado e a próxima execução vai falhar. Você corrige isso escrevendo direito as pré-condições e setup do seu teste. Por simplicidade eu deixarei como está, ok?

Tudo isso funciona corretamente se você está desenvolvendo sozinho, mas e se houver uma equipe junto com você?

2 - Em primeiro lugar, os arquivos não serão levados juntos com o projeto, então se alguém adiciona outro arquivo para teste, o que você faz? Isso garoto, adicione os arquivos ao projeto de teste, para todos os desenvolvedores terem acesso ao distinto quando pegarem uma nova versão do projeto.

3 - (Um problema de verdade) Cada desenvolvedor pode definir um diretório local diferente para seu workspace de trabalho, então se um desenvolvedor colocar os arquivos da solução em outro diretório e mudar o App.Config, quando você pegar a última versão do projeto no TFS, bye bye testes!

Uma maneira de "resolver" isso é ficar mudando o App.Config para cada desenvolvedor ou então padronizar o diretório local para o seu projeto, mas é um gato MUITO feio. Concorda? E se houverem builds diários, você vai fazer o quê? Como resolvemos isso?


  1. Adicione ao seu projeto o diretório "Arquivos" e coloque lá dentro o famoso "arquivoqualquer.txt". Lembre de marcar a opção do arquivo "Copy to output directory" com "Copy if newer".

  2. Como a cada nova execução dos testes são gerados novos diretórios com um timestamp diferente (ex.: "C:\Projects\VisualStudio\TestesComArquivos\TestResults\luciano.moreira_DSKSRN01 2009-10-26 14_26_18"), precisamos referenciar os arquivos dentro do diretório "Out", onde estão as DLLs do projeto.

    1. Como nossa propriedade em Configuração.RepositórioArquivos é somente leitura e não queremos interferir com a interface da classe, adicionarei um método internal chamado DefineRepositórioArquivos.

      internal static void DefineRepositórioArquivos(string repositório) {
      repositórioArquivos = repositório;
      }

    2. Para que esse método seja visível no nosso projeto de testes, utilizamos um pequeno recurso do .NET, definindo que os métodos internal do assembly de negócio são visíveis somente para o projeto de testes. Recurso que somente utilizo nesse tipo de cenário.

      [assembly: InternalsVisibleTo("TestesComArquivos_TesteSuite")]

    3. A partir desse momento eu faço uma pequena alteração no setup do meu teste, para que ele defina qual o repositório de arquivos de acordo com o diretório de deployment do teste. Para isso eu utilizo a classe auxiliar TestContext, que possui a propriedade DeploymentDirectory. Se você não quiser usar essa classe auxiliar, pode partir para a ignorância com o AppDomain.CurrentDomain.BaseDirectory.

      TestesComArquivos.Configuração.DefineRepositórioArquivos(TestContext.DeploymentDirectory);

    Se você executar o seu teste nesse momento irá receber um erro! Analisando com cuidado verificará que o problema está no primeiro Assert, que pergunta pela existência do arquivo, então olhando o diretório criado pelos testes notará que o arquivo não foi colocado no Out. Huummm, mas no passo 1 você já pediu para o arquivo ser copiado para o diretório de saída, não é suficiente?


    O pior é que não! Quando você compila o projeto de teste o arquivo é colocado no "\Bin\Debug" corretamente, mas não é levado para o "\TestResult\....\Out" que é criado para o teste. Para isso é necessário editar as configurações dos testes em "Local.testsettings" (dentro de Solution Items) e no Deployment adicionar o diretório "Arquivos", conforme figura abaixo.




  3. Pronto!

    Basta executar o seu teste que tudo vai funcionar e quantas vezes forem necessárias, pois cada novo teste copia o arquivo original para um novo diretório, evitando o primeiro problema que eu citei. Agora todos os seus desenvolvedores podem trabalhar tranquilamente, adicionar novos arquivos ao diretório já criado e codificar novos testes, basta lembrar de definir o diretório correto no início dos testes.

    Bom, espero que tenham gostado. O projeto que criei está disponível juntamente com o PDF do artigo. Baixe aqui.

    []s
    Luciano Caixeta Moreira - {Luti}
    Chief Innovation Officer Sr. Nimbus Serviços em Tecnologia Ltda luciano.moreira@srnimbus.com.br
    www.twitter.com/luticm