Tornando o código interno, mas disponível para testes de unidade de outros projetos

129

Colocamos todos os nossos testes de unidade em seus próprios projetos. Nós achamos que temos que tornar certas classes públicas em vez de internas apenas para os testes de unidade. Existe alguma maneira de evitar ter que fazer isso. Quais são as implicações de memória tornando as classes públicas em vez de seladas?

leora
fonte
2
possível duplicata de C # modificador de acesso "interno" ao fazer o teste de unidade
Dour High Arch

Respostas:

205

Se você estiver usando o .NET, o atributo de montagem InternalsVisibleTo permitirá criar assemblies "amigos". Estes são assemblies específicos com nomes fortes que têm permissão para acessar classes internas e membros do outro assembly.

Observe que isso deve ser usado com discrição, pois une firmemente as montagens envolvidas. Um uso comum para InternalsVisibleTo é para projetos de teste de unidade. Provavelmente não é uma boa opção para uso em seus conjuntos de aplicativos reais, pelo motivo exposto acima.

Exemplo:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
Cinza
fonte
23
Eu sugiro colocar um #if DEBUG em torno do atributo e, em seguida, teste de unidade na depuração. Dessa forma, você terá certeza de que o atributo não está definido no código de liberação.
Steve Cook
Isso é apenas uma idéia, eu não sei ... Que tal: #if DEBUG classe pública IniReader #else classe interna IniReader #endif Provavelmente não é recomendado? Por quê?
jmelhus
4
Bem, por que limitar testes a depurações de compilações?
Marco Mp
2
Além disso, apenas um resumo, não é necessário que as assembléias de "amigos" sejam fortemente nomeadas (pode ser uma boa prática - mesmo que meu gosto pessoal diga o contrário, mas não obrigatório).
Marco Mp
9
Discordo. Se estou criando um componente complexo com uma API pública muito fina, é impraticável e irrealista testar apenas através da API pública. Você vai acabar com uma bola de barro inatingível. Em vez disso, eu definiria cuidadosamente as unidades internas e as testaria separadamente. Como Jeremy D. Miller disse com bastante frequência: "Teste pequeno antes de testar grande".
Dennis Doomen
6

Se é uma classe interna, não deve ser usada isoladamente. Portanto, você realmente não deveria testá-lo além de testar alguma outra classe que faz uso desse objeto internamente.

Assim como você não deve testar membros particulares de uma classe, não deve testar classes internas de uma DLL. Essas classes são detalhes de implementação de alguma classe acessível ao público e, portanto, devem ser bem exercitadas através de outros testes de unidade.

A idéia é que você só quer testar o comportamento de uma classe, porque se você testar detalhes de implementação internos, seus testes serão frágeis. Você deve poder alterar os detalhes de implementação de qualquer classe sem interromper todos os seus testes.

Se você achar que realmente precisa testar essa classe, convém reexaminar por que essa classe é interna em primeiro lugar.

Josh
fonte
2
Os detalhes da implementação devem ser exercidos como parte de um teste abrangente. Não espie variáveis ​​particulares ... teste o comportamento esperado. Se o teste estiver correto .. todo o encanamento e a fiação interna devem ser testados como parte dele. Votado.
Gishu 20/09/08
69
Eu não concordo necessariamente com isso como essas classes são "público" para outras classes dentro da DLL ea funcionalidade da classe deve ser testado indepdently
Leora
27
Eu também não concordo. Unidades são unidades e devem ser testadas isoladamente.
Sentinel
5
Ótima orientação geral, mas não sejamos dogmáticos. Os testes têm dois objetivos principais: 1) Regressão - verifique se você não quebrou alguma coisa; 2) Aumentando a velocidade do desenvolvimento. Se eu tiver que oferecer um serviço enorme toda vez que quiser escrever uma linha de código, dificultarei o desenvolvimento. Se eu tenho uma peça interna complexa, quero poder desenvolver e testar isoladamente. Por outro lado, não quero que tudo seja testado isoladamente (diminuindo o valor do teste de regressão ou duplicando o código de teste), mas algum código justifica o isolamento. Talvez a chave seja transformá-lo em uma montagem separada.
Lee Jensen
2
OP aqui está correto. Você está acoplando seus testes à implementação interna. Portanto, sua equipe é para sempre uma escrava para consertar os testes e o código de zombaria horrível. Teste apenas APIs públicas, seja a API da biblioteca empacotada ou APIs expostas à rede. Todo o código deve ser exercitado pela porta da frente. Testar a implementação é o motivo pelo qual o TDD morreu em grande parte; é apenas uma enorme PITA testar literalmente todas as classes de todo o aplicativo, em vez de se concentrar em garantir comportamentos do sistema. Eu acho que quase todos, incluindo livros "oficiais", entenderam isso errado ou não.
Luke Puplett
4

para fins de documentação

Como alternativa, você pode instanciar a classe interna usando o Type.GetTypemétodo

exemplo

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

para o tipo genérico, existem processos diferentes como abaixo:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});
ktutnik
fonte
-5

As aulas podem ser públicas e seladas.

Mas não faça isso.

Você pode criar uma ferramenta para refletir sobre classes internas e emitir uma nova classe que acessa tudo via reflexão. O MSTest faz isso.

Editar: quero dizer, se você não quiser incluir -qualquer coisa de teste em sua montagem original; isso também funciona se os membros forem privados.

TraumaPony
fonte
1
Espere o que? Você está dizendo que não faz um public sealed class? Qual é o seu raciocínio para essa jóia?
esmagar
@crush TraumaPony não logado desde 2009.
Prof. Falken