En mi anterior entrada mostraba distintas alternativas que podíamos utilizar para volver al siguiente fragmento de código fácilmente testeable. Obviamente existe una infinidad de alternativas que no he abordado como los frameworks de aislamiento, los servidores de smtp que no envían los mails y muchas más.
La idea aquí es mostrar el por qué este código no es un duro de testear. Obviamente es porque tiene una dependencia con la clase SmtpClient la cual se comunica mediante un socket con el ambiente externo.
Entonces, el problema radica en la sentencia:
Ahora bien, en algunos lenguajes como por ejemplo, Ruby. El mismo código es fácilmente testeable ya que las clases e incluso las instancias están abiertas para la modificación en tiempo de ejecución. Entonces en Ruby podemos sobre escribir el método “new” de manera que nos devuelva un tipo diferente al que le pedimos. ¿Podemos en C# hacer lo mismo? Ummm…. la respuesta correcta es no, pero podemos acercarnos bastante. Fijate si notas la diferencia entre el siguiente código y el primero:
¿La encontraste? Bien! Este código ahora si es testeable. De la siguiente manera:
A simple vista se parece mucho a una sobrecarga del operador “new”, también podría compararse con un contenedor de inversión de control o con una factoría. No es mi intención discutir ese tema sino mostrar como las características nuevas de c# 4 sobre el mundo dinámico nos puede ayudar a pensar en otros tipos de soluciones.
Les dejo todo el código (es solo una prueba de concepto):
using System;
using System.Collections.Generic;
using System.Net.Mail;
namespace CSharpDi
{
public class Program
{
public static void Main()
{
typeof(SmtpClient).RegisterBuilder(p =>
new
{
Send = new Action<string, string, string, string>(
(from, to, subject, body) => { })
}
);
var tm = new TranferManager();
tm.Tranfer();
}
}
public class TranferManager
{
public void Tranfer()
{
// Perform the required actions
var smtpClient = New. SmtpClient();
smtpClient.Send("info@bank.com", "from.Email", "Tranfer", "?");
}
}
public static class New
{
public static dynamic SmtpClient(params object[] parameters)
{
return typeof(SmtpClient).New(parameters);
}
}
public static class CreationExtensions
{
private static Dictionary<Type, Func<object, dynamic>> builders =
new Dictionary<Type, Func<object, dynamic>>();
public static dynamic New(this Type type, params object[] parameters)
{
if(builders.ContainsKey(type))
return builders[type](parameters);
return Activator.CreateInstance(type, parameters);
}
public static void RegisterBuilder(this Type type, Func<object, dynamic> builder)
{
builders.Add(type, builder);
}
}
}