Respostas:
Acredito que uma abordagem padrão para isso é usar um padrão de fachada para envolver o gerenciador de configuração e, em seguida, você terá algo fracamente acoplado sobre o qual tem controle.
Então você agruparia o ConfigurationManager. Algo como:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Você pode apenas extrair uma interface da sua classe de configuração e, em seguida, usá-la em qualquer lugar do seu código). Em seguida, você apenas zomba da configuração ICon. Você poderá implementar a própria fachada de algumas maneiras diferentes. Acima, eu escolhi apenas envolver as propriedades individuais. Você também obtém o benefício colateral de ter informações fortemente digitadas para trabalhar com matrizes de hash fracamente digitadas.
var configurationMock = new Mock<IConfiguration>();
e para a configuração:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Estou usando o AspnetMvc4. Um momento atrás eu escrevi
ConfigurationManager.AppSettings["mykey"] = "myvalue";
no meu método de teste e funcionou perfeitamente.
Explicação: o método de teste é executado em um contexto com as configurações do aplicativo retiradas, normalmente a web.config
ou myapp.config
. ConfigurationsManager
pode alcançar esse objeto global de aplicativo e manipulá-lo.
Porém: se você tem um executor de testes executando testes em paralelo, isso não é uma boa ideia.
ConfigurationManager.AppSettings
é um NameValueCollection
que não é seguro para threads, portanto testes paralelos usando-o sem sincronização adequada não são uma boa ideia. Caso contrário, você pode simplesmente chamar ConfigurationManager.AppSettings.Clear()
seu TestInitialize
/ ctor e você estará dourado.
Talvez não seja o que você precisa realizar, mas você já pensou em usar um app.config em seu projeto de teste? Portanto, o ConfigurationManager obterá os valores que você coloca no app.config e você não precisa zombar de nada. Essa solução funciona bem para minhas necessidades, porque nunca preciso testar um arquivo de configuração "variável".
Web.config
abrangendo o projeto. Durante o teste, extrair alguns valores conhecidos do app.config
é muito válido. O teste de unidade só precisa garantir que as condições quando ele puxa, digamos "cluster1", funcionem; existem apenas 4 grupos diferentes neste caso.
Você pode usar calços para modificar AppSettings
para um NameValueCollection
objeto personalizado . Aqui está um exemplo de como você pode conseguir isso:
[TestMethod]
public void TestSomething()
{
using(ShimsContext.Create()) {
const string key = "key";
const string value = "value";
ShimConfigurationManager.AppSettingsGet = () =>
{
NameValueCollection nameValueCollection = new NameValueCollection();
nameValueCollection.Add(key, value);
return nameValueCollection;
};
///
// Test code here.
///
// Validation code goes here.
}
}
Você pode ler mais sobre calços e falsificações em Isolando código sob teste com o Microsoft Fakes . Espero que isto ajude.
Você já pensou em fazer stub em vez de zombar? A AppSettings
propriedade é uma NameValueCollection
:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Arrange
var settings = new NameValueCollection {{"User", "Otuyh"}};
var classUnderTest = new ClassUnderTest(settings);
// Act
classUnderTest.MethodUnderTest();
// Assert something...
}
}
public class ClassUnderTest
{
private readonly NameValueCollection _settings;
public ClassUnderTest(NameValueCollection settings)
{
_settings = settings;
}
public void MethodUnderTest()
{
// get the User from Settings
string user = _settings["User"];
// log
Trace.TraceInformation("User = \"{0}\"", user);
// do something else...
}
}
Os benefícios são uma implementação mais simples e nenhuma dependência do System.Configuration até que você realmente precise.
IConfiguration
como Joshua Enfield sugere pode ser um nível muito alto e você pode perder bugs que existem devido a coisas como análise de valor de configuração incorreta. Por outro lado, o uso ConfigurationManager.AppSettings
direto como LosManos sugere é um detalhe de implementação demais, sem mencionar que pode ter efeitos colaterais em outros testes e não pode ser usado em execuções de teste paralelas sem sincronização manual (como NameValueConnection
não é seguro para threads).
Essa é uma propriedade estática e o Moq é projetado para métodos ou classes de instância do Moq que podem ser zombados por herança. Em outras palavras, o Moq não ajudará você aqui.
Para zombar de estática, eu uso uma ferramenta chamada Moles , que é gratuita. Existem outras ferramentas de isolamento de estrutura, como o Typemock, que também podem fazer isso, embora eu acredite que sejam ferramentas pagas.
Quando se trata de estática e testes, outra opção é criar você mesmo o estado estático, embora isso possa ser problemático (como eu imagino que seria no seu caso).
E, finalmente, se as estruturas de isolamento não são uma opção e você está comprometido com essa abordagem, a fachada mencionada por Joshua é uma boa abordagem ou qualquer abordagem em geral em que você considere o código do cliente longe da lógica comercial que você usa. estou usando para testar.
Eu acho que escrever o seu próprio provedor app.config é uma tarefa simples e é mais útil do que qualquer outra coisa. Especialmente, você deve evitar falsificações, como calços, etc., porque assim que usá-las, o Editar e Continuar não funciona mais.
Os provedores que uso são assim:
Por padrão, eles obtêm os valores dos App.config
testes de unidade, mas eu posso substituir todos os valores e usá-los em cada teste independentemente.
Não há necessidade de nenhuma interface ou implemente-a sempre. Eu tenho uma DLL de utilitários e uso esse pequeno auxiliar em muitos projetos e testes de unidade.
public class AppConfigProvider
{
public AppConfigProvider()
{
ConnectionStrings = new ConnectionStringsProvider();
AppSettings = new AppSettingsProvider();
}
public ConnectionStringsProvider ConnectionStrings { get; private set; }
public AppSettingsProvider AppSettings { get; private set; }
}
public class ConnectionStringsProvider
{
private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public string this[string key]
{
get
{
string customValue;
if (_customValues.TryGetValue(key, out customValue))
{
return customValue;
}
var connectionStringSettings = ConfigurationManager.ConnectionStrings[key];
return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString;
}
}
public Dictionary<string, string> CustomValues { get { return _customValues; } }
}
public class AppSettingsProvider
{
private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public string this[string key]
{
get
{
string customValue;
return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key];
}
}
public Dictionary<string, string> CustomValues { get { return _customValues; } }
}