Acessando membros internos via System.Reflection?

Eu estou tentando Unit Test uma class que tem muitas funções internas. Obviamente, eles também precisam de testes, mas meu projeto de testes é separado, principalmente porque abrange muitos projetos pequenos e relacionados. O que eu tenho até agora é:

FieldInfo[] _fields = typeof(ButtonedForm.TitleButton).GetFields( BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); Console.WriteLine("{0} fields:", _fields.Length); foreach (FieldInfo fi in _fields) { Console.WriteLine(fi.Name); } 

Isto cospe todos os membros privados bem, mas ainda não exibe internals. Eu sei que isso é possível, porque quando eu estava brincando com os testes gerados automaticamente que o Visual Studio pode produzir, ele perguntou sobre algo a ver com a exibição de internos para o projeto de teste. Bom, agora estou usando o NUnit e realmente gostando, mas como posso conseguir a mesma coisa com ele?

Seria mais apropriado usar o atributo InternalsVisibleTo para conceder access aos membros internos do assembly ao seu assembly de teste de unidade.

Aqui está um link com algumas informações adicionais úteis e um passeio por:

  • As maravilhas dos InternalsVisibleTo

Para realmente responder à sua pergunta … Interno e protegido não são reconhecidos na API de Reflexão do .NET. Aqui está uma cotação da MSDN :

As palavras-chave C # protegidas e internas não têm significado em IL e não são usadas nas APIs de reflection. Os termos correspondentes em IL são Família e Montagem. Para identificar um método interno usando o Reflection, use a propriedade IsAssembly . Para identificar um método interno protegido, use o IsFamilyOrAssembly .

Adicionando o atributo de nível de assembly InternalsVisibleTo ao seu projeto principal, com o nome do Assembly do thre projeto de teste deve tornar os membros internos visíveis.

Por exemplo, adicione o seguinte à sua assembly fora de qualquer class:

 [assembly: InternalsVisibleTo("AssemblyB")] 

Ou para uma segmentação mais específica:

 [assembly:InternalsVisibleTo("AssemblyB, PublicKey=32ab4ba45e0a69a1")] 

Observe que, se o seu assembly de aplicativo tiver um nome forte, seu assembly de teste também precisará ser fortemente nomeado.

Eu acho que você precisa perguntar se você deve escrever testes de unidade para methods privados? Se você escrever testes de unidade para seus methods públicos, com uma cobertura de código ‘razoável’, você já não está testando nenhum método privado que precise ser chamado como resultado?

Amarrar testes a methods privados tornará os testes mais frágeis. Você deve ser capaz de alterar a implementação de qualquer método privado sem quebrar nenhum teste.

Refs:

http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx http://richardsbraindump.blogspot.com/2008/08/should-i-unit-test-private-methods.html http://junit.sourceforge.net/doc/faq/faq.htm#tests_11 http://geekswithblogs.net/geekusconlivus/archive/2006/07/13/85088.aspx

Seu código está exibindo apenas campos. Por isso, espero que ele não mostre nenhum membro interno, pois os campos devem ser sempre IMO particulares. (Com a potencial exceção de constantes.)

O ButtonedForm.TitleButton tem algum campo não privado? Se você está tentando encontrar methods internos , então obviamente você precisa chamar GetMethods (ou GetMembers ) para chegar até eles.

Como outros sugeriram, o InternalsVisibleTo é muito útil para testes (e quase exclusivamente para testes!). Quanto a saber se você deve testar methods internos – certamente acho útil poder fazer isso. Eu não considero o teste de unidade como sendo exclusivamente um teste de checkbox preta. Muitas vezes, quando você sabe que a funcionalidade pública é implementada usando alguns methods internos conectados de maneira simples, é mais fácil realizar testes completos de cada um dos methods internos e alguns testes de “pseudo-integração” no método público.

Uma justificativa para usar o InternalsVisible está sob as minhas circunstâncias. Nós compramos o código-fonte para um controle gráfico. Descobrimos onde precisamos fazer algumas modificações no controle de origem e compilar nossa própria versão. Agora, para garantir que não quebramos nada, existem alguns testes de unidade que preciso escrever que precisam de access a alguns campos internos.

Este é um caso perfeito onde o InternalsVisible faz sentido.

Eu estava pensando, no entanto, o que você faz se você não tem access à fonte? Como você pode chegar a um campo interno? .Net Reflector pode ver esse código, mas eu acho que é só olhar para o IL.