Propriedades de stubbing com setters particulares para testes


10

Nós temos o objeto

public class MyObject{
    protected MyObject(){}

    public string Property1 {get;private set;}
    public string Property2 {get;private set;}
    public string Property3 {get;private set;}
    public string Property4 {get;private set;}
    public string Property5 {get;private set;}
    public string Property6 {get;private set;}
    public string Property7 {get;private set;}
    public string Property8 {get;private set;}
    public string Property9 {get;private set;}
    public string Property10 {get;private set;}
}

Em nosso código de produção, preenchemos esse objeto através do automapper. Ele pode acessar as propriedades e configurá-las corretamente.

Agora, quando queremos testar esta classe em um pipeline futuro, não é possível preencher as propriedades com valores fictícios (a serem testados).

Existem algumas opções disponíveis.

  • Construtores personalizados para aceitar os parâmetros necessários para os testes e definir as propriedades, atualmente são necessários 3 construtores. Isso não está limpo, pois os construtores não atendem a nenhuma funcionalidade comercial.

  • Torne as propriedades virtuais para que a classe possa ser removida. Mas marcar as propriedades como virtuais não fornece nenhum valor comercial e polui minha classe.

  • Adicione um construtor de objetos à classe para construir internamente o objeto. Novamente, nenhum valor agregado aos negócios. Talvez um pouco mais limpo, mas ainda muito código não relevante nos objetos de domínio.

Alguma sugestão, conselho ou opções alternativas aqui?

Respostas:


8

Você tem várias opções.

  • Vá em frente e use construtores personalizados, propriedades virtuais ou um construtor de objetos. A lógica por trás disso é que um objeto deve permanecer por si só e não depender de alguma mágica como o automapper. Uma classe que é completamente inútil, a menos que haja alguma mágica acontecendo, não é um pensamento muito bom da classe. "Valor comercial" não é o único determinante de um bom design.

  • Inclua o automapper no processo de teste. Alguns dirão que isso não é mais um teste de unidade. Isso não importa. O teste de unidade não é o único tipo de teste.

  • Implemente algo que forneça a funcionalidade do automapper especificamente para teste. Você pode escrever facilmente um pequeno utilitário que utilizará a reflexão para preencher seu objeto a partir de um dicionário que contém nomes e valores de propriedades.

Também dê uma olhada nesta pergunta e resposta: Você prefere tornar coisas privadas internas / públicas para testes ou usar algum tipo de invasão como o PrivateObject?


3

Não hesito em usar a reflexão para coisas assim em testes.

Não gosto de tornar as coisas virtuais para zombar, pois altera o código pelo motivo errado.

Não conheço o automapper, mas concordo com o @Mike que incluí-lo nos testes pode ser uma boa ideia. O teste unitário de distinção / teste de integração não é muito interessante. Certifique-se de que o conjunto de testes esteja ficando grande e lento, você precisará filtrar e classificar as coisas para executar apenas um subconjunto sensato de todos os testes na frequência mais alta.

Amostra de hack usando reflexão, usando nameof () terá melhor desempenho, mas você perde tipos:

public static class TestExtensions
{
    public static void SetProperty<TSource, TProperty>(
        this TSource source,
        Expression<Func<TSource, TProperty>> prop,
        TProperty value)
    {
        var propertyInfo = (PropertyInfo)((MemberExpression)prop.Body).Member;
        propertyInfo.SetValue(source, value);
    }
}

0

Para fins de teste de unidade, use estruturas de simulação, como Microsoft Fakes, TypeMock e JustMock, que fornecem suporte para a simulação de membros privados.

Por favor, dê uma olhada no Smocks (pacotes @nuget disponíveis) também. A limitação das blusas é que ela não fornecerá acesso a membros privados. Mas tem a capacidade de zombar de membros estáticos e não virtuais. Também está disponível gratuitamente.

Outra maneira mais simples é usar, PrivateObject / PrivateType.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.