ti-enxame.com

A estática é universalmente "ruim" para testes de unidade e, em caso afirmativo, por que o Resharper recomenda?

Eu descobri que existem apenas três maneiras de dependências de teste de unidade (mock/stub) que são estáticas no C # .NET:

Dado que dois deles não são gratuitos e um não atingiu a versão 1.0, zombar de coisas estáticas não é muito fácil.

Isso torna métodos estáticos e "mal" (no sentido de teste de unidade)? E se sim, por que o re-afiador quer que eu faça algo que possa ser estático, estático? (Assumir que o re-compartilhador também não é "mau".)

Esclarecimento: Estou falando do cenário em que você deseja testar um método por unidade e esse método chama um método estático em um diferente unidade/classe. Na maioria das definições de teste de unidade, se você apenas permitir que o método em teste chame o método estático na outra unidade/classe, você não teste de unidade, você está integração teste. (Útil, mas não um teste de unidade.)

87
Vaccano

Olhando para as outras respostas aqui, acho que pode haver alguma confusão entre métodos estáticos que mantêm um estado estático ou causam efeitos colaterais (que me parecem uma péssima idéia) e métodos estáticos que apenas retornam um valor.

Os métodos estáticos que não mantêm nenhum estado e não causam efeitos colaterais devem ser facilmente testados por unidade. De fato, considero esses métodos uma forma de programação funcional "pobre"; você entrega ao método um objeto ou valor e ele retorna um objeto ou valor. Nada mais. Não vejo como esses métodos afetariam negativamente o teste de unidade.

108
Robert Harvey

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

Não há nada errado com os 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 é uma boa candidata a uma classe estática com métodos estáticos. Se você possui um método (artificial) 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 passa, digamos, 20, recebe 400. Não há problema.

27
Dan Diplo

Se a pergunta real aqui for "Como faço para testar este código?":

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

Em seguida, basta refatorar o código e injetar como de costume a chamada para a classe estática assim:

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();
    }
}
18
Sunny

A estática não é necessariamente ruim, mas pode limitar suas opções quando se trata de teste de unidade com falsificações/zombarias/stubs.

Existem duas abordagens gerais para zombar.

O primeiro (tradicional - implementado pela RhinoMocks, Moq, NMock2; zombarias e tocos manuais também estão neste campo) se baseia em costuras de teste e injeção de dependência. Suponha que você esteja testando unitariamente algum código estático e que tenha dependências. O que acontece frequentemente no código projetado dessa maneira é que a estática cria suas próprias dependências, invertendo a inversão de dependência . Você logo descobre que não pode injetar interfaces simuladas no código em teste projetado dessa maneira.

O segundo (simulado qualquer coisa - implementado por TypeMock, JustMock e Moles) depende do .NET Profiling API . Ele pode interceptar qualquer uma das instruções do CIL e substituir um pedaço do seu código por um falso. Isso permite que o TypeMock e outros produtos deste campo zombem de qualquer coisa: estática, classes seladas, métodos particulares - coisas que não foram projetadas para serem testáveis.

Há um debate em andamento entre duas escolas de pensamento. Diz-se que siga os princípios do SOLID e projete a testabilidade (que geralmente inclui a facilidade com a estática). O outro diz: compre TypeMock e não se preocupe.

15
azheglov

Verifique isto: "Os métodos estáticos são de morte para testabilidade" . Breve resumo do argumento:

Para testar a 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 mesmo que apenas chamem outros métodos estáticos.

14
Rafał Dowgird

A verdade simples raramente reconhecida é que, se uma classe contiver uma dependência visível do compilador em outra classe, ela não poderá será testada 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 quaisquer 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 do compilador e você não pode testar validamente sem ela. Qualquer pedaço menor é simplesmente não é uma unidade testável válida. Qualquer tentativa de tratá-lo como se tivesse resultados não terá mais significado do que escrever um pequeno utilitário para emitir o XML usado por sua estrutura de teste para dizer 'teste aprovado'.

Dado isso, existem três opções:

  1. defina o teste de unidade como sendo o 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 as classes que você é responsável por testar. 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 para a qual você precisa usar o termo teste de integração.

5
soru

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ê tem um método estático e precisa removê-lo, não pode, a menos que use uma estrutura de isolamento baseada em perfil. Uma solução compatível com chamada é 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 DE:

    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, em seu código de teste de unidade, você pode stubar essa chamada da seguinte maneira (supondo que ela esteja em uma classe chamada Database):

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

Tome cuidado para substituí-lo pelo valor original após concluir. Você pode fazer isso através de uma tentativa/finalmente ou, na limpeza do teste de unidade, a chamada após cada teste, escreva um código como este:

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

que reinvocará o inicializador estático da sua classe.

Os Funda Lambda 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 for um método de extensão, primeiro você deverá alterá-lo para um método sem extensão. O Recarregador pode fazer isso por você automaticamente.
  2. Se algum dos tipos de dados dos métodos estáticos for um assembly de interoperabilidade incorporada, como para o Office, você precisará agrupar o método, agrupar 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ção do Resharper.

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

Portanto, na realidade, qualquer pessoa que sugerir o remédio para stubbing métodos estáticos deve torná-los métodos de instância, eles também estariam 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 ele permite métodos de instância 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 espere 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 liga para isso, mas se você puder sobre o desempenho de uma chamada indireta, esse é outro motivo para evitar métodos de instância. Há casos em que é um sucesso de desempenho. É por isso que existem métodos não virtuais em primeiro lugar.

4
zumalifeguard

Vejo que, depois de muito tempo, ninguém ainda declarou um fato realmente simples. Se o re-afiador me disser que eu posso tornar um método estático, isso significa uma coisa enorme para mim, eu posso ouvir a voz dele me dizer: "ei, você, essas peças de lógica não são a RESPONSABILIDADE da classe atual de lidar, por isso deve ficar de fora em alguma classe auxiliar ou algo assim ".

3
g1ga
  1. Acredito que seja em parte porque os métodos estáticos são "mais rápidos" de chamar do que os métodos de instância. (Entre aspas, porque isso cheira a micro otimização), consulte http://dotnetperls.com/static-method
  2. Ele está dizendo que ele não precisa de estado, portanto pode ser chamado de qualquer lugar, removendo a sobrecarga da instatiação, se essa é a única coisa que alguém precisa.
  3. Se eu quiser zombar, geralmente acho que é a prática declarada em uma interface.
  4. Se for declarado em uma interface, o R # não sugerirá que você a torne estática.
  5. Se declarado virtual, o R # também não sugere que você o torne estático.
  6. Manter estado (campos) estaticamente é algo que sempre deve ser considerado com cuidado . O estado estático e os fios se misturam como lítio e água.

O R # não é a única ferramenta que fará essa sugestão. A análise de código FxCop/MS também fará o mesmo.

Eu diria que, se o método é estático, geralmente deve ser testável como também. Isso traz alguma consideração de design e provavelmente mais discussão do que eu tenho agora, aguardando pacientemente os votos negativos e comentários ...;)

3
MIA

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

E se esse método estático fala com a Internet, conecta bancos de dados, mostra pop-ups da GUI ou converte o teste de unidade em uma bagunça completa, ele simplesmente funciona sem nenhum trabalho fácil. Um método que chama esse método estático não pode ser testado sem a refatoração, mesmo que tenha muito código puramente computacional que se beneficiaria muito com o teste de unidade.

2
h22

Acredito que o Resharper esteja fornecendo orientações e aplique as diretrizes de codificação com as quais foi configurado. Quando eu uso o Resharper e ele me diz 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ê também não deve testar métodos privados.

Quanto à testabilidade de métodos estáticos que são públicos, o teste de unidade fica difícil quando métodos estáticos tocam no 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 podem ser controladas através de um dispositivo de teste. No entanto, esta é uma decisão de design.

0
aqwert