Respuestas:
Creo que un enfoque estándar para esto es usar un patrón de fachada para envolver el administrador de configuración y luego tienes algo vagamente acoplado sobre el que tienes control.
Entonces, envolvería el ConfigurationManager. Algo como:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Puede extraer una interfaz de su clase de configuración y luego usar esa interfaz en todas partes en su código) Luego simplemente se burla de la IConfiguration. Es posible que pueda implementar la fachada en sí de diferentes maneras. Arriba elegí solo para envolver las propiedades individuales. También obtiene el beneficio adicional de tener información fuertemente tipada para trabajar en lugar de matrices hash tipadas débilmente.
var configurationMock = new Mock<IConfiguration>();
y para la configuración:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Estoy usando AspnetMvc4. Hace un momento escribí
ConfigurationManager.AppSettings["mykey"] = "myvalue";
en mi método de prueba y funcionó perfectamente.
Explicación: el método de prueba se ejecuta en un contexto con la configuración de la aplicación tomada, generalmente, una web.config
o myapp.config
. ConfigurationsManager
puede alcanzar este objeto global de aplicación y manipularlo.
Sin embargo: si tiene un corredor de prueba que ejecuta pruebas en paralelo, no es una buena idea.
ConfigurationManager.AppSettings
es un sistema NameValueCollection
que no es seguro para subprocesos, por lo que las pruebas paralelas que lo usan sin la sincronización adecuada no son una buena idea de todos modos. De lo contrario, puede llamar ConfigurationManager.AppSettings.Clear()
a su TestInitialize
/ ctor y estará dorado.
Tal vez no sea lo que necesita lograr, pero ¿ha considerado usar una app.config en su proyecto de prueba? Por lo tanto, ConfigurationManager obtendrá los valores que pones en app.config y no necesitas burlarte de nada. Esta solución funciona bien para mis necesidades, porque nunca necesito probar un archivo de configuración "variable".
Web.config
proyecto que lo abarca. Durante las pruebas, extraer algunos valores bien conocidos del app.config
es muy válido. La prueba de la unidad solo necesita asegurarse de que las condiciones cuando se tira dicen "cluster1" funciona; solo hay 4 grupos diferentes en este caso.
Puede usar cuñas para modificar AppSettings
a un NameValueCollection
objeto personalizado . Aquí hay un ejemplo de cómo puede lograr esto:
[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.
}
}
Puede leer más sobre las laminillas y falsificaciones en Aislar el código bajo prueba con falsificaciones de Microsoft . Espero que esto ayude.
¿Has considerado tropezar en lugar de burlarte? La AppSettings
propiedad es un 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...
}
}
Los beneficios son una implementación más simple y sin dependencia del sistema. Configuración hasta que realmente lo necesite.
IConfiguration
como sugiere Joshua Enfield, puede ser un nivel demasiado alto, y es posible que se pierdan errores que existen debido a cosas como un análisis de valor de configuración incorrecto. Por otro lado, usar ConfigurationManager.AppSettings
directamente como sugiere LosManos es demasiado detalle de implementación, sin mencionar que puede tener efectos secundarios en otras pruebas y no puede usarse en pruebas paralelas sin sincronización manual (ya NameValueConnection
que no es seguro para subprocesos).
Esa es una propiedad estática, y Moq está diseñado para métodos o clases de instancia de Moq que se pueden burlar mediante herencia. En otras palabras, Moq no te será de ninguna ayuda aquí.
Para simular estadísticas, uso una herramienta llamada Moles , que es gratuita. Hay otras herramientas de aislamiento de marcos, como Typemock que también pueden hacer esto, aunque creo que son herramientas pagas.
Cuando se trata de estadísticas y pruebas, otra opción es crear el estado estático usted mismo, aunque esto a menudo puede ser problemático (como, imagino, sería en su caso).
Y, finalmente, si los marcos de aislamiento no son una opción y usted está comprometido con este enfoque, la fachada mencionada por Joshua es un buen enfoque, o cualquier enfoque en general en el que factorice el código de cliente de esto lejos de la lógica comercial que usted estás usando para probar.
Creo que escribir su propio proveedor de app.config es una tarea simple y es más útil que cualquier otra cosa. Especialmente debe evitar cualquier falsificación como cuñas, etc. porque tan pronto como los use Editar y continuar ya no funciona.
Los proveedores que uso se ven así:
De manera predeterminada, obtienen los valores de las App.config
pruebas unitarias, pero puedo anular todos los valores y usarlos en cada prueba de forma independiente.
No hay necesidad de ninguna interfaz o implementarla una y otra vez. Tengo un dll de utilidades y uso este pequeño ayudante en muchos proyectos y pruebas unitarias.
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; } }
}