É estático universalmente "mal" para testes unitários e, se sim, por que Resharper o recomenda? [fechadas]

82

Eu descobri que há apenas 3 maneiras de depender de unidades de teste (mock / stub) que são estáticas em C # .NET:

Dado que dois deles não são gratuitos e um não atingiu o lançamento 1.0, não é muito fácil zombar de material estático.

Isso faz com que os métodos estáticos e tal "mal" (no sentido de teste de unidade)? E se sim, por que o resharper quer que eu faça algo que pode ser estático, estático? (Assumir que o resharper também não é "mal").

Esclarecimento: Eu estou falando sobre o cenário quando você quer testar um método e esse método chama um método estático em uma diferente unidade / classe. Pela maioria das definições de testes unitários, se você deixar o método em teste chamar o método estático na outra unidade / classe, então você está testando a unidade, você está testando a integração. . (Útil, mas não um teste de unidade).

    
por Vaccano 21.09.2010 / 03:18
fonte

11 respostas

102

Olhando as outras respostas aqui, acho que pode haver alguma confusão entre os métodos estáticos que mantêm o estado estático ou causam efeitos colaterais (o que me parece muito ruim) e métodos estáticos que simplesmente retornam um valor.

Métodos estáticos que não possuem estado e não causam efeitos colaterais devem ser facilmente testáveis em unidades. Na verdade, considero esses métodos uma forma de programação funcional "pobre"; você entrega o método a um objeto ou valor e retorna um objeto ou valor. Nada mais. Não vejo como esses métodos afetariam negativamente o teste unitário.

    
por 21.09.2010 / 17:00
fonte
24

Você parece estar confundindo dados estáticos e métodos estáticos . Resharper, se bem me lembro, recomenda tornar métodos private dentro de uma classe estática se eles puderem ser feitos - acredito que isso produz um pequeno benefício de desempenho. Não não recomenda fazer "qualquer coisa que possa ser" estática!

Não há nada errado com métodos estáticos e eles são fáceis de testar (desde que não alterem nenhum dado estático). Por exemplo, pense em uma biblioteca de matemática, que é boa candidata para uma classe estática com métodos estáticos. Se você tem um método (planejado) como este:

public static long Square(int x)
{
    return x * x;
}

então isso é eminentemente testável e não tem efeitos colaterais. Você apenas verifica que quando você passa, digamos, 20, você recebe de volta 400. Não tem problema.

    
por 21.09.2010 / 17:29
fonte
17

Se a questão real aqui for "Como testo este código?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Em seguida, refatore o código e injete como de costume a chamada para a classe estática da seguinte forma:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
    
por 15.08.2011 / 15:26
fonte
14

Confira: "Métodos estáticos são morte para testabilidade" . Breve resumo do argumento:

Para o teste de unidade, você precisa pegar um pequeno pedaço do seu código, reconectar suas dependências e testá-lo isoladamente. Isso é difícil com métodos estáticos, não apenas no caso de eles acessarem o estado global, mas até mesmo se apenas chamarem outros métodos estáticos.

    
por 21.09.2010 / 09:37
fonte
13

As estatísticas não são necessariamente más, mas podem limitar suas opções quando se trata de testes unitários com falsificações / mocks / stubs.

Existem duas abordagens gerais para zombar.

O primeiro (tradicional - implementado pelo RhinoMocks, Moq, NMock2; simulações manuais e stubs neste campo também) se baseia em costuras de teste e injeção de dependência. Suponha que você esteja testando um código estático e tenha dependências. O que acontece frequentemente no código projetado dessa forma é que as estatísticas criam suas próprias dependências, invertendo inversão de dependência . Você logo descobre que não é possível injetar interfaces ridicularizadas no código em teste projetado dessa maneira.

O segundo (mock anything - implementado por TypeMock, JustMock e Moles) depende da API de criação de perfis do .NET . Ele pode interceptar qualquer uma das suas instruções CIL e substituir uma parte do seu código por uma falsa. Isso permite que o TypeMock e outros produtos neste campo façam qualquer coisa: estática, classes seladas, métodos privados - coisas que não foram projetadas para serem testadas.

Existe um debate em andamento entre duas escolas de pensamento. Um diz, siga Princípios SOLID e projete para testabilidade (que geralmente inclui ficar mais fácil em estática). O outro diz, compre o TypeMock e não se preocupe.

    
por 21.09.2010 / 19:23
fonte
5

A verdade simples raramente reconhecida é que, se uma classe contiver uma dependência visível pelo compilador em outra classe, não poderá ser testado isoladamente dessa classe. Você pode falsificar algo que parece um teste e aparecerá em um relatório como se fosse um teste.

Mas não terá as principais propriedades definidoras de um teste; falhando quando as coisas estão erradas, passando quando estão certas.

Isso se aplica a todas as chamadas estáticas, chamadas de construtor e qualquer referência a métodos ou campos não herdados de uma classe ou interface base . Se o nome da classe aparecer no código, é uma dependência visível pelo compilador e você não pode validamente testar sem ele. Qualquer pedaço menor simplesmente é não uma unidade testável válida . Qualquer tentativa de tratá-lo como se fosse, terá resultados não mais significativos do que escrever um pequeno utilitário para emitir o XML usado por sua estrutura de teste para dizer "teste passado".

Considerando que existem três opções:

  1. defina o teste de unidade como teste da unidade composta por uma classe e suas dependências codificadas. Isso funciona, desde que você evite dependências circulares.

  2. nunca crie dependências em tempo de compilação entre classes pelas quais você é responsável pelo teste. Isso funciona, desde que você não se importe com o estilo de código resultante.

  3. não teste de unidade, teste de integração. O que funciona, desde que não entre em conflito com outra coisa que você precise usar o termo teste de integração.

por 01.03.2015 / 21:07
fonte
4

Não há duas maneiras sobre isso. As sugestões do ReSharper e vários recursos úteis do C # não seriam usados com tanta frequência se você estivesse escrevendo testes de unidade atômica isolados para todo o seu código.

Por exemplo, se você tiver um método estático e precisar apagá-lo, não será possível, a menos que você use uma estrutura de isolamento baseada em perfil. Uma solução alternativa compatível com chamadas é alterar a parte superior do método para usar a notação lambda. Por exemplo:

ANTES:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

DEPOIS:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

Os dois são compatíveis com chamadas. Os chamadores não precisam mudar. O corpo da função permanece o mesmo.

Em seguida, no seu código Unit-Test, você pode resumir esta chamada assim (supondo que esteja em uma classe chamada Database):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Tenha o cuidado de substituí-lo pelo valor original depois de terminar. Você pode fazer isso através de um try / finally ou, em sua limpeza de teste de unidade, aquele que é chamado após cada teste, escreva um código como este:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

que irá invocar novamente o inicializador estático da sua classe.

Os Lambda Funcs não são tão ricos em suporte quanto os métodos estáticos regulares, portanto, essa abordagem tem os seguintes efeitos colaterais indesejáveis:

  1. Se o método estático era um método de extensão, você deve primeiro alterá-lo para um método que não seja de extensão. O Resharper pode fazer isso automaticamente.
  2. Se qualquer um dos tipos de dados dos métodos estáticos for um assembly de interoperabilidade incorporada, como o Office, você precisará quebrar o método, encapsular o tipo ou alterá-lo para o tipo 'objeto'.
  3. Você não pode mais usar a ferramenta de refatoração de assinatura de alterações do Resharper.

Mas digamos que você evite completamente a estática e converta isso em um método de instância. Ainda não é compatível, a menos que o método seja virtual ou implementado como parte de uma interface.

Portanto, na realidade, qualquer um que sugira o remédio para cortar métodos estáticos é torná-los métodos de instância, eles também seriam contra métodos de instância que não são virtuais ou fazem parte de uma interface.

Então, por que o C # tem métodos estáticos? Por que permite métodos de instâncias não virtuais?

Se você usar um desses "Recursos", simplesmente não poderá criar métodos isolados.

Então, quando você os usa?

Use-os para qualquer código que você não espera que alguém queira apagar. Alguns exemplos:     o método Format () da classe String     o método WriteLine () da classe Console     o método Cosh () da classe Math

E mais uma coisa ... A maioria das pessoas não se preocupa com isso, mas se você puder sobre o desempenho de uma chamada indireta, essa é outra razão para evitar métodos de instância. Há casos em que é um impacto no desempenho. É por isso que existem métodos não virtuais em primeiro lugar.

    
por 01.03.2015 / 15:42
fonte
3
  1. Acredito que seja parcialmente porque os métodos estáticos são "mais rápidos" para chamar do que os métodos de instância. (Entre aspas porque isso cheira a micro otimização) veja link
  2. Ele está dizendo a você que não precisa de estado, portanto, pode ser chamado de qualquer lugar, removendo a sobrecarga de instauração se essa é a única coisa que alguém precisa.
  3. Se eu quiser zombar, acho que geralmente é a prática que é declarada em uma interface.
  4. Se for declarado em uma interface, o R # não sugerirá que você o torne estático.
  5. Se for declarado virtual, o R # não sugerirá que você o torne estático.
  6. Manter o estado (campos) estaticamente é algo que sempre deve ser considerado cuidadosamente . O estado estático e os fios se misturam como lítio e água.

R # não é a única ferramenta que fará essa sugestão. FxCop / MS Code Analysis também fará o mesmo.

Eu geralmente diria que, se o método é estático, geralmente ele deve ser testado como está. Isso traz algumas considerações de design e, provavelmente, mais discussão do que eu tenho em meus dedos agora, tão pacientemente aguardando os votos e comentários ...;)

    
por 21.09.2010 / 05:38
fonte
3

Eu vejo que depois de muito tempo ninguém ainda afirmou um fato muito simples. Se resharper me diz que eu posso fazer um método estático, isso significa uma coisa enorme para mim, eu posso ouvir sua voz me dizendo: "Ei, você, esses pedaços de lógica não são RESPONSABILIDADE da classe atual para lidar, por isso deve ficar de fora em alguma classe auxiliar ou algo do tipo ".

    
por 29.11.2012 / 18:08
fonte
2

Se o método estático é chamado de dentro de outro método , não é possível evitar ou substituir tal chamada. Isso significa que esses dois métodos formam uma única unidade; Teste unitário de qualquer tipo testa os dois.

E se este método estático se comunica com a Internet, conecta bancos de dados, mostra pop-ups da GUI ou converte o teste da Unidade em uma bagunça completa, isso simplesmente acontece sem um trabalho fácil. Um método que chama tal método estático não é testável sem refatoração, mesmo que tenha muito código puramente computacional que seja altamente beneficiado por ser testado em unidade.

    
por 18.10.2015 / 09:20
fonte
0

Eu acredito que o Resharper está dando a você um guia e aplicando as diretrizes de codificação com as quais ele foi configurado. Quando eu usei o Resharper e ele me disse que um método deve ser estático, ele deve estar em um método privado que não atua em nenhuma variável de instância.

Agora, quanto à testabilidade, esse cenário não deve ser um problema, pois você não deve testar métodos privados de qualquer maneira.

Quanto à capacidade de teste de métodos estáticos que são públicos, o teste de unidade torna-se difícil quando os métodos estáticos atingem o estado estático. Pessoalmente, eu manteria isso no mínimo e usaria métodos estáticos como funções puras, tanto quanto possível, onde quaisquer dependências são passadas para o método que pode ser controlado por meio de um dispositivo de teste. No entanto, esta é uma decisão de design.

    
por 21.09.2010 / 05:38
fonte