Temos uma camada de lógica de negócios (BLL) fortemente acoplada à nossa camada de acesso a dados (DAL). Fazemos chamadas assim:
using (FooData data = new FooData())
{
data.DoSomething();
}
É importante observar que todas as nossas classes de dados estão internal
e estão no mesmo assembly que as classes lógicas, para que apenas o BLL possa acessar o DAL.
Para dissociá-los (para ajudar a facilitar o teste de unidade), uma idéia é criar interfaces IDataClass, como o IFooData. No começo, pensei que poderia ser um problema, porque teríamos que tornar nossas classes de dados públicas para implementar as interfaces. Mas devemos ser capazes de manter as classes de dados internas, mesmo que ainda precisemos que os métodos sejam públicos:
public interface IFooData
{
void DoSomething();
}
internal class FooData : IFooData // Notice the class is internal
{
public void DoSomething(); // Method is public, but that's ok because class is internal
}
Portanto, mesmo que o método seja público, uma vez que a própria classe é interna, ainda devemos permitir apenas o acesso à BLL.
Existe algo inerentemente errado com essa abordagem? Existe uma maneira melhor de abstrair o DAL, para testes de unidade, sem abrir o DAL para o mundo tornando-o público?
fonte
Respostas:
Extrair uma interface e garantir que a BLL esteja usando apenas tipos de interface é um bom caminho a seguir para tornar a BLL testável sem o banco de dados.
O que você está fazendo está absolutamente bem.
fonte
using (IFooData data = new FooData())
no seu BLL, você realmente não fez nenhuma alteração, exceto pela introdução da interface. Agora você terá que encontrar uma maneira de instanciarIFooData
externamente, caso contrário, seus testes de BLL ainda serão acoplados ao DAL.DataAccessFactory.GetDataInterface<IFooData>().DoSomething();
Você pode usar o
InternalsVisibleToAttribute
para separar seu DAL da camada de negócios, mas ainda assim manter os métodos DAL internos. Você acabará criando seu DAL como um assembly de amigo .Essa pode não ser uma ótima abordagem, pois você precisaria especificar no
Assembly.cs
arquivo do seu DAL quais assemblies podem acessar o DAL. Portanto, ainda há algum acoplamento envolvido (DAL sabendo sobre BLL).Portanto, para responder sua pergunta, não há nada de errado com sua abordagem. Porém, tirar proveito dos assemblies de amigos pode fornecer um pouco mais de abstração e provavelmente ajudá-lo a tornar seu código mais testável.
Eu espero que isso ajude.
fonte
InternalsVisibleTo
, se eu precisar mover o DAL para outra montagem, isso realmente ajudaria.Você parece imóvel em suas idéias que expôs em sua pergunta, mas eu proponho uma resposta de qualquer maneira - alguém pode achar útil em algum momento no futuro.
Se você insistir em manter seu DAL interno, mas ainda desejar atingi-lo com alguns testes de unidade e não se importar de usar as ferramentas de Teste de Unidade da Microsoft, tente a abordagem PrivateObject . Esse é um invólucro para reflexão que você sempre pode codificar a si mesmo, mas economiza algumas linhas de digitação.
Observe que o uso de InternalsVisibleTo é uma abordagem viável, mas é desajeitada - se você a estiver usando, poderá já estar em uma descida rápida ladeira abaixo.
internal
significa que algo é público apenas dentro dos limites de sua montagem; esse modificador é normalmente usado porque você não deseja itens utilizáveis de foranessa montagem, você imediatamente se vira e viola esse conceito declarando InternalsVisibleTo ... que é uma grande bandeira vermelha suja que está clamando por alguma refatoração. A dissociação é um desperdício de tempo quando apenas uma montagem está chamando a outra, você também pode colá-las. A dissociação deduz que vários assemblies estão chamando o destino (a introdução do teste de unidade não conta para o 'múltiplo' porque existem maneiras de chegar ao assembly de destino, como eu já mencionei).fonte
InternalsVisibleTo
. Eu não estou usando isso. Por alguma razão, você está assumindo que estou usando. Eu não entendo o porquê.Tecnicamente, não há nada de errado com seu design.
No entanto, eu consideraria uma abordagem alternativa: em vez de dissociar sua BLL do seu DAL, desacople-o melhor do seu banco de dados. Permita a criação de objetos DAL na memória e, se sua BLL precisar carregar objetos DAL de algum lugar, use o padrão de repositório (veja aqui ) para evitar o contato direto da BLL com o banco de dados. Dessa forma, você pode
De fato, o que você perde é a capacidade de testar o BLL com zombarias DAL. Mas, na realidade, esses objetos simulados do DAL geralmente se parecem muito com os objetos reais do DAL para fornecer uma base para testes úteis da BLL. IMHO evita muita duplicação de código e esforço desnecessário ao reutilizar seus objetos DAL reais para testes automatizados da BLL.
fonte
A abordagem de interface é boa / mainstream para zombar do seu DAL durante o teste de consumidores como o BLL.
No entanto, uma coisa a ser observada é que, ao tornar suas classes de concreto DAL internas, você pode tornar seu DAL de concreto mais difícil de testar diretamente, pois isso agora implica que seus testes de unidade de DAL também devem estar nesta montagem sem a necessidade de usar portas traseiras como reflexão.
Além disso, em algum momento no futuro, você pode considerar o uso de um contêiner de IoC para construir a implementação por trás de uma interface, que também pode desarmar o interno.
Você pode imitar o encapsulamento que está tentando alcançar com 'apenas interno' adicionando uma regra de convenção / LINT que sua BLL / outras camadas devem acoplar ao DAL por meio da interface, não na implementação concreta.
fonte