Modificador de acesso "interno" do C # ao realizar testes de unidade

469

Eu sou novo em testes de unidade e estou tentando descobrir se devo começar a usar mais modificadores de acesso 'internos'. Sei que se usarmos 'internal' e definirmos a variável de montagem 'InternalsVisibleTo', poderemos testar funções que não queremos declarar públicas do projeto de teste. Isso me faz pensar que eu sempre deveria usar 'interno' porque pelo menos cada projeto (deveria?) Tem seu próprio projeto de teste. Vocês podem me dizer uma razão pela qual eu não deveria fazer isso? Quando devo usar 'privado'?

Hertanto Lie
fonte
1
Vale mencionar - muitas vezes você pode evitar a necessidade de testar a unidade de seus métodos internos usando System.Diagnostics.Debug.Assert()os próprios métodos.
Mike Marynowski 30/03/19

Respostas:

1195

As classes internas precisam ser testadas e há um atributo de montagem:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Adicione isso ao arquivo de informações do projeto, por exemplo Properties\AssemblyInfo.cs.

EricSchaefer
fonte
70
Adicione-o ao projeto em teste (por exemplo, em Properties \ AssemblyInfo.cs). "MyTests" seria o conjunto de teste.
9119 EricSchaefer
86
Esta deve realmente ser a resposta aceita. Eu não sei sobre vocês, mas quando os testes estão "muito longe" do código, eles tendem a ficar nervosos. Eu sou a favor de evitar testar qualquer coisa marcada como private, mas muitas privatecoisas podem muito bem apontar para uma internalclasse que está lutando para ser extraída. TDD ou nenhum TDD, eu prefiro ter mais testes que testam muito código do que ter poucos testes que exercitam a mesma quantidade de código. E evitar testar internalcoisas não ajuda exatamente a alcançar uma boa proporção.
sm
7
Há uma grande discussão entre @DerickBailey e Dan Tao sobre a diferença semântica entre interno e privado e a necessidade de testar componentes internos . Vale a pena ler.
Kris McGinnes
31
A quebra automática de blocos e #if DEBUG, o #endifbloco ativará essa opção apenas nas compilações de depuração.
O verdadeiro Edward Cullen
17
Essa é a resposta correta. Qualquer resposta que diga que apenas métodos públicos devem ser testados em unidade está faltando o objetivo dos testes de unidade e dando uma desculpa. O teste funcional é orientado por caixa preta. Os testes de unidade são orientados por caixa branca. Eles devem testar "unidades" de funcionalidade, não apenas APIs públicas.
Gunnar
127

Se você quiser testar métodos particulares, dê uma olhada PrivateObjecte PrivateTypeno Microsoft.VisualStudio.TestTools.UnitTestingespaço para nome. Eles oferecem wrappers fáceis de usar em torno do código de reflexão necessário.

Documentos: PrivateType , PrivateObject

Para o VS2017 e 2019, você pode encontrá-los baixando o nuget MSTest.TestFramework

Brian Rasmussen
fonte
15
Ao votar, por favor, deixe um comentário. Obrigado.
Brian Rasmussen
35
É estúpido votar nesta resposta. Aponta para uma nova solução e realmente boa, não mencionada anteriormente.
Ignacio Soler Garcia
Aparentemente, há algum problema w / usando o TestFramework para aplicativo alvo .net2.0 ou mais recente: github.com/Microsoft/testfx/issues/366
Johnny Wu
41

Adicionando à resposta de Eric, você também pode configurar isso no csprojarquivo:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Ou, se você tiver um projeto de teste por projeto a ser testado, poderá fazer algo assim no seu Directory.Build.propsarquivo:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Consulte: https://stackoverflow.com/a/49978185/1678053
Exemplo: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12

gldraphael
fonte
2
Essa deve ser a principal resposta imo. Todas as outras respostas estão muito desatualizadas, pois o .net está se afastando das informações de montagem e movendo a funcionalidade para as definições de csproj.
mBrice1024
12

Continue usando privado por padrão. Se um membro não deve ser exposto além desse tipo, ele não deve ser exposto além desse tipo, mesmo dentro do mesmo projeto. Isso mantém as coisas mais seguras e organizadas - quando você está usando o objeto, fica mais claro quais métodos você deve usar.

Dito isto, acho razoável tornar os métodos naturalmente privados internos para fins de teste às vezes. Eu prefiro isso do que usar reflexão, que é refatoração hostil.

Uma coisa a considerar pode ser um sufixo "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Então, quando você estiver usando a classe dentro do mesmo projeto, é óbvio (agora e no futuro) que você realmente não deveria usar esse método - ele está lá apenas para fins de teste. Isso é um pouco hacky, e não é algo que eu mesmo faço, mas pelo menos vale a pena considerar.

Jon Skeet
fonte
2
Se o método for interno, isso não impede o uso do conjunto de teste?
Ralph Shillington
7
Ocasionalmente uso a ForTestabordagem, mas sempre a acho feia (adicionar código que não fornece valor real em termos de lógica de negócios de produção). Normalmente eu acho que eu tinha que usar a abordagem porque o projeto é somwhat infeliz (ou seja, ter de repor as instâncias únicas entre os testes)
ChrisWue
1
Tentou diminuir o voto - qual é a diferença entre esse hack e simplesmente tornar a classe interna em vez de privada? Bem, pelo menos com condicionais de compilação. Então fica muito confuso.
Bloke CAD
6
@ CADBLoke: Você quer dizer tornar o método interno e não privado? A diferença é que é óbvio que você realmente deseja que seja privado. Qualquer código na sua base de código de produção que chame um método ForTestestá obviamente errado, enquanto que, se você apenas tornar o método interno, parece bom usá-lo.
precisa
2
@CADbloke: Você pode excluir métodos individuais em uma versão compilada com a mesma facilidade no mesmo arquivo que o uso de classes parciais, IMO. E se você fizer isso, isso sugere que você não está executando seus testes contra a versão do lançamento, o que me parece uma má ideia.
precisa
11

Você também pode usar private e chamar métodos privados com reflexão. Se você estiver usando o Visual Studio Team Suite, ele possui uma funcionalidade interessante que irá gerar um proxy para chamar seus métodos particulares. Aqui está um artigo de projeto de código que demonstra como você pode fazer o trabalho sozinho para testar métodos privados e protegidos:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

Em termos de qual modificador de acesso você deve usar, minha regra geral é começar com privado e escalar conforme necessário. Dessa forma, você expõe o mínimo possível de detalhes internos da sua classe e isso ajuda a manter os detalhes da implementação ocultos, como deveriam.

Steven Behnke
fonte
3

Estou usando Dotnet 3.1.101e as .csprojadições que funcionaram para mim foram:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Espero que isso ajude alguém lá fora!

Sunfish flutuante
fonte
1
A adição de gerar explicitamente informações de montagem foi o que finalmente a fez funcionar para mim também. Obrigado por postar isso!
thevioletsaber 11/03