IDisposable Thoughts

Honey… where is my coding t-shirt?

Hey there! Thanks for dropping by Theme Preview! Take a look around
and grab the RSS feed to stay updated. See you around!

Posts Tagged ‘programming’

PHP y Microsoft como plataforma

Hace unas semanas fui invitado por parte de Microsoft a conversar un poco en el DrupalCamp 2010 que se realizó en Guatemala, quizás muchos no lo saben pero en “una vida pasada” fui developer en PHP por mucho tiempo (ufff… tiempos aquellos). Bien, fue interesante regresar a hablar sobre el soporte en que ahora ha trabajado Microsoft en su “stack” junto con PHP, transformando a Microsoft Windows no solamente en una excelente plataforma de desarrollo para PHP, sino también en una fuerte plataforma de ejecución.

Como lo había prometido (pero un poco tarde esta vez) los dejo con las diapositivas de la charla, quizás algún otro día conversamos un poco más sobre PHP (si me diera tiempo claro).

.NET y Configuraciones – Parte 8

La última vez que conversamos acerca de configuraciones sacamos a relucir lo sencillo que es agregar soporte para funciones de validación callback, y antes de eso comentábamos lo sencillo que era agregar soporte para validación de forma declarativa usando los atributos de validación incluídos en la Configuration Framework de la .Net Framework.

Hoy seguiremos caminando en el soporte de validación customizada de secciones y elementos de validación, pero esta vez creando nuestros propios atributos de validación customizados.

Atributos de Validación

Un atributo de validación nos permite validar los elementos o secciones de nuestra configuración de .Net, simplemente tenemos que adornar ese elemento con el atributo en cuestión. La .Net framework nos incluye algunos atributos ya dentro de la caja, pero podemos agregar los nuestros propios de forma sencilla.

Realmente un atributo de validación costa de dos clases: La clase que marca el atributo (un atributo marcador) y la clase que ejecuta la acción de validar (el validador). Como podremos notar en este lado de la framework se utilizan los atributos de una manera muy particular (y que en lo personal me gusta) de mezclar el atributo como marcador pero no como ejecutor.

Usemos un simple ejemplo, imaginemos que necesitamos validar la entrada de correos electrónicos, como todos sabemos, podemos seguir un par de reglas expuestas en una serie de RFC’s acerca de direcciones de email. Bueno, comencemos por la clase que hace realmente la validación, esta debe heredar de la clase ConfigurationValidatorBase y luego el override de los métodos CanValidate (que nos dice si podemos o no validar el tipo en cuestión) y Validate (quien realmente realiza la validación).

using System;
using System.Configuration;
using System.Text.RegularExpressions;

namespace Cprieto.Samples {
    public class EmailValidator : ConfigurationValidatorBase {
        private const string Word = "[^\\x00-\\x1F^\\(^\\)^\\<^\\>^\\@^\\,^\\;^\\:^\\\\^\\\"^\\.^\\[^\\]^\\s]";
        private const string IpEntry = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
        private static readonly string Domain = string.Format("({0}+(\\.{0}+)*", Word);

        private static readonly Regex EmailRegex =
            new Regex(string.Format("^{0}+(\\.+)*@{1}|{2})$", Word, Domain, IpEntry), RegexOptions.Compiled);

        public override bool CanValidate(Type type) {
            return type == typeof (string);
        }

        public override void Validate(object value) {
            var item = (string) value;
            if (!string.IsNullOrEmpty(item) && !EmailRegex.IsMatch(item))
                throw new ArgumentException("value is not a valid email");
        }
    }
}

Bien, ahora simplemente creamos el atributo marcador, este debe heredar de ConfigurationValidatorAttribute y la parte importante de este es el override de la propiedad ValidatorInstance que retorna una nueva instancia de nuestra clase validadora.

using System.Configuration;

namespace Cprieto.Samples {
    public class EmailValidatorAttribute : ConfigurationValidatorAttribute {
        public override ConfigurationValidatorBase ValidatorInstance {
            get { return new EmailValidator(); }
        }
    }
}

Ahora simplemente lo aplicamos a nuestra sección de configuración:

using System.Configuration;

namespace Cprieto.Samples {
    public class SampleConfigurationSection : ConfigurationSection {
        [CallbackValidator(Type = typeof(PortValidator),
            CallbackMethodName = "Validate")]
        [ConfigurationProperty("port", DefaultValue = 80)]
        public int Port {
            get { return (int) this["port"]; }
        }

        [ConfigurationProperty("host", IsRequired = true)]
        public string Host {
            get { return (string) this["host"]; }
        }

        [EmailValidator]
        [ConfigurationProperty("email")]
        public string Email {
            get { return (string) this["email"]; }
        }
    }
}

Y claro, nunca cae de más un pequeño archivo de validación que debe marcar como inválido

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="sample"
             type="Cprieto.Samples.SampleConfigurationSection, Cprieto.Samples.ValidationCallback"/>
  </configSections>
  <sample port="80" host="localhost" email="invalid@@email" />
</configuration>

Bueno, y ahora me queda pensar que escribo para el siguiente post de la serie :) Hasta la próxima!

Cambiando el usuario de un commit en Mercurial

Hoy mi gran amigo José Romaniello (@jfroma) me abarcó con una pregunta acerca de Mercurial un poco curiosa pero bastante bien fundada:

Por un error anterior, necesito cambiar el nombre de usuario que aparece registrado en algunos commits de mi repositorio de Mercurial por otro usuario, cómo puedo hacerlo? ¿tengo que reescribir la historia del repositorio?

Digamos que al principio de mis commits utilizaba el nombre de Santa Clos (digamos que así estaba la máquina con que comencé a trabajar el proyecto) y luego decidí cambiar el nombre a mi correcto nombre de Cristian Prieto, bueno, si vemos el log de commits tendremos algo como lo siguiente:

Powershell-2010-07-06_19.20.15

Bueno, si quieren pueden intentar cambiar la historia con Hg mq, pero una manera mucho más sencilla si tienen su historial local es usar a nuestro amigo “Convert”.

Algunos creen que convert (la cual es una extensión de Mercurial incluida en la instalación) simplemente sirve para convertir de un sistema de control de versiones a otro (entiéndase de Subversion a Mercurial, como nos lo muestra nuestro amigo Fabio), la verdad también nos permite convertir de un repositorio de Mercurial a otro, y en el transcurso de la conversión cambiar los valores de cada uno de los commits, entre ellos, el author del commit. En este caso en particular basta con crear un archivo “authors” que contenga la lista de valores de conversión de los autores, es un simple archivo de texto con retorno de carro. En este caso tendremos un archivo con este simple contenido:

Santa Clos <santa@northpole.net>=Cristian Prieto <me@cprieto.com>

Ahora asegurémonos que tenemos nuestra extensión convert activada, revisen que en su Mercurial.ini tengan la siguiente línea

[extensions]
hgext.convert=

Y basta solamente convertir nuestros repositorios

hg convert --authors authors.txt orig changed

Bien, ahora si revisamos el historial nos quedaría bello, como esperábamos :)

Powershell-2010-07-06_19.28.20

Para saber un poco más acerca de convert, basta con la página del wiki de Mercurial :)

Mapeo, DTO’s y ViewModels en ASP.NET MVC

Los DTO’s –Data Transfer Objects- (algunos los llaman ViewModels/EditModels según el uso para el que existan) nacen por una razón específica: El modelo de lo que ves en la vista no representa directamente tu modelo de negocio. Mi ejemplo tradicional siempre ha sido el formulario de Login/Usuario/Contraseña.

Con el pasar del tiempo mi postura hacia este tema ha sido variada y se ha hecho bastante flexible. Por ejemplo, si tu modelo y tu aplicación es lo suficientemente sencilla y sabes que luego de eso jamás se va a modificar ni cambiar entonces es suficientemente fácil quedarte con el modelo y permearlo hacia la vista y vice versa, eso es precisamente lo que hace la generación Rails/Django/Whatever y lo que solemos hacer con MonoRail y Castle ActiveRecord, ¿la razón? simple, el modelo es sencillo, el dominio es sencillo y no es un proyecto complicado.

Para bien o para mal, en el entorno en que muchos se mueven no se ven cosas tan estáticas y los modelos y reglas de negocio son mucho más complejos siendo generalmente nuestros modelos de negocio , de hecho, si trasladamos eso a una visión de diseño orientado a servicio las cosas cambian aún más (ni hablemos de una "DDD shop"), lugares donde vemos que esta regla se revienta es en sistemas empresariales al estilo CRM/ERP o las siglas que sean…

En el contexto de una aplicación MVC se sobreentiende que el viewmodel es un concern de la vista, por lo tanto esta restringido a los conceptos de presentación. Recordemos también que un controller también es un concern de la vista (si, para aquellos que siguen creyendo que el controlador es la lógica de negocio, les vuelvo a romper el corazón en este momento), un buen lugar para que vivan los viewmodels es dentro de la aplicación MVC, surge el problema entonces, ¿cómo mapeo de un modelo de negocio al modelo de la vista?, bueno, les presento un par de ideas.

Usando el mapper como un servicio

Podemos usar un mapper automático como nuestro intermediario en el controlador entre el modelo de dominio y el viewmodel y de esta manera separar e injectar el mapper en el controlador

public class OrdersController : BaseController {
    readonly IOrdersRepository _ordersRepository;
    readonly IOrdersMapper _mapper;

    public OrdersController(IOrdersRepository ordersRepository, IOrdersMapper mapper) {
        _ordersRepository = ordersRepository;
        _mapper = mapper;
    }

    public ViewResult List() {
        var orders = _ordersRepository.FindAll();
        View(_mapper.Map(orders));
    }
}

Usando un Application Service

En este caso el Application Service se encarga de tomar las decisiones y luego retornar hacia el controlador los resultados, en este caso el controlador no sabe nada de la entidad original del dominio ni sobre el repositorio original, este es sumamente flexible cuando nuestro dominio original suele cambiar drásticamente o tomar decisiones muy complejas para delegárselas al controlador. En este caso es posible que a su vez el mapper sea inyectado y sea una dependencia del servicio. De hecho, podría ser que el servicio no use un mapper para nada, si no que simplemente arme el resultado usando varias uniones de otros resultados sobre la marcha (usando LINQ por ejemplo).

public interface IOrdersService {
    IEnumerable<OrderView> GetOrders();
}

public class OrdersController : BaseController {
    readonly IOrdersService _orderService;
    public OrdersController(IOrdersService orderService) {
        _orderService =  orderService;
    }

    public ViewResult List() {
        var orders = orderService.GetOrders();
        View(orders);
    }
}

Delegando el mapeo a un ModelBinder y ActionFilter

Debo admitir que aunque este es uno de mis resultados o formas favoritas de hacerlo en una aplicación MVC, lo siento a mi criterio demasiado “opionated” (entiéndase, fumado o cargado). En este escenario no es responsabilidad directa ni de un mapper ni de un servicio el retornar los resultados esperados, si no de un Model Binder junto con un ActionFilter en ASP.NET MVC. El controlador luciría algo así:

public class OrdersController : BaseController {
    readonly IOrdersRepository _ordersRepository;

    public OrdersController(IOrdersRepository ordersRepository) {
        _ordersRepository = ordersRepository;
    }

    [Automap(typeof(Order), typeof(OrderView)]
    public ViewResult List() {
        var orders = _ordersRepository.FindAll();
        View(orders);
    }
}

Este último caso es bastante flexible pero involucra que conozcamos bien que es un Model Binder y un ActionFilter, claro, nada que un par de minutos al frente de Visual Studio no pueda solucionar :)

Debo admitir que el caso de usar un Application Service me parece más natural si nuestra aplicación es simplemente un front end a una infraestructura orientada más hacia servicios que hacia un modelo directo de consumo (entiéndase, cosas como un ERP/CRM que probablemente usemos sus interfaces por otro lado y no solamente en nuestra aplicación de “CRUD”) y es mucho más flexible para casos en que necesitamos aislar completamente la “orquestración” del resultado del “despliegue” del resultado, suena extraño al principio pero se me ocurren bastantes casos donde esto se puede presentar.

Debo hacer notar que estas observaciones son meramente orientadas al contexto de una aplicación en ASP.NET MVC y no directamente se pueden trasladar a una aplicación o a un Tier en WCF, en este caso lo único que debemos compartir entre servicios es su esquema, no sus objetos.

Como siempre, espero sus comentarios o críticas al respecto, siempre son bienvenidas, recuerden, estamos en este barco del aprendizaje todos juntos como marineros.

¡Saludos!

UPDATE: Mi amigo José Romaniello me comenta del uso de una interface estilo IMapper en vez de una especializada para mapeo (como lo es IOrdersMapper), este patrón de uso lo considero igual al uso de una especializada, por lo tanto no la incluyo.

UPDATE 2: No incluyo implementaciones definidas de como crear estos mappers, para esto se puede hacer de forma directa o usando una herramienta de mapeo de clases como Automapper, pero eso lo dejo para otro post futuro.

FluentNhibernate y Sql-query

Hoy en la oficina uno de mis compañeros de trabajo me presenta un simple problema muy particular

Necesito llamar a un procedimiento almacenado desde NHibernate, pero estoy usando FluentNhibernate para el mapeo de entidades, ¿hay algo que puedo hacer?

No es algo así como un problema “clásico” el tener que llamar procedimientos almacenados en Nhibernate, pero creo que vale la pena explicarlo.

Digamos que el procedimiento almacenado sea algo como esto

CREATE PROCEDURE [dbo].[usp_GetItemCount](@parentId INT) AS
BEGIN
    SELECT COUNT(*) AS [Total]
    FROM [items]
    WHERE [parent_id] = @parentId
END

Bueno, nada complicado, pero digamos que necesitamos llamarlo desde algún lugar de nuestra aplicación. Digamos que debido a que es algo fuera de la entidad de negocio necesitamos hacerlo usando un servicio. Bueno, escribamos el servicio

public interface ICountService {
    int GetTotal(int parentId);
}

Como recordaran, en NHibernate existe algo llamado named queries utilizadas para ejecutar consultas de SQL arbitrarias en el motor de base de datos, es una herramienta sumamente poderosa, les recomiendo dar una vuelta por la documentación. Una named query se define en un archivo de mapeo HBM al igual que los archivos de mapeo de NHibernate, por lo tanto podemos definir esta named query en el archivo Queries.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <sql-query name="SampleQuery" cacheable="true"
             read-only="true" callable="true">
    <query-param name="parentId" type="Int32"/>
    <return-scalar type="Int32" column="Total"/>
    exec usp_GetItemCount @parentId = :parentId
  </sql-query>
</hibernate-mapping>

Ok señores, pero digamos que estamos usando Fluent NHibernate para el mapeo de nuestras entidades… Rayos… Bueno, no tanto, en realidad es bastante sencillo, en Fluent Nhibernate tenemos la opción de agregar mapeos de XML al mapeo "fluent"

SessionFactory =
 Fluently.Configure().Database(MsSqlConfiguration.MsSql2008
     .ConnectionString(
         c => c.Server("(local)").Database("Test")
             .TrustedConnection())
         .ShowSql().CurrentSessionContext("web")
         .ProxyFactoryFactory<ProxyFactoryFactory>())
     .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ItemClassMap>())
     .Mappings(m => m.HbmMappings.AddFromAssemblyOf<ItemClassMap>())
     .BuildSessionFactory();

Digamos que el archivo de mapeo de la query se encuentra en el mismo lugar que el de las classmaps de fluent nhibernate. Ahora es sencillo definir el servicio que ejecuta el procedimiento almacenado.

public class CountServiceImpl : ICountService {
    readonly ISessionFactory _factory;
    protected ISession Session {
        get { return _factory.GetCurrentSession(); }
    }

    public CountServiceImpl(ISessionFactory factory) {
        _factory = factory;
    }

    public int GetTotal(int parentId) {
        var total = Session.GetNamedQuery("SampleQuery")
            .SetInt32("parentId", parentId)
            .UniqueResult<int>();
        return total;
    }
}

Simple, sencillo, llano, conciso…

Como siempre para comentarios o lo que quieran decir/compartir/discutir se encuentra la cajita de comentarios en este post… Hasta la próxima!

Enumeradores – Bit Flags

Todos recordamos que podemos usar los enumeradores como flags y luego recuperar la selección con operaciones de bit, por ejemplo, tomemos el siguiente enumerador (tomado de la documentación de MSDN):

[Flags]
public enum MultiHue {
    Black = 0,
    Red = 1,
    Green = 2,
    Blue = 4
}

Podemos pedirle al usuario de la API que seleccione un set de colores o tonalidades y luego obtener su selección mediante bit operations:

var hue = MultiHue.Black | MultiHue.Red;
var isRed = ((hue & MultiHue.Red) == MultiHue.Red);
var isGreen = ((hue & MultiHue.Green) == MultiHue.Green);

Bien, ahora en la .Net Framework 4.0 tenemos un nuevo método que nos hace mas “claro” la lectura de la bit flag:

var hue = MultiHue.Black | MultiHue.Red;
var isRed = hue.HasFlag(MultiHue.Red);
var isGreen = hue.HasFlag(MultiHue.Green);

No se ustedes pero este último es de mejor lectura para mi.

¡Saludos!

Lo nuevo en ASP.NET 4.0, Introducción

El lanzamiento de Visual Studio 2010 y con ello la .Net Framework 4.0 esta a la vuelta de la esquina, con cada nuevo lanzamiento (desde que recuerdo usar la .Net Framework, o sea, tampoco mucho tiempo atrás :P ) siempre me propongo como meta el escribir una serie de posts acerca de lo “nuevo” que trae y los cambios que hay que observar. Esta vez estoy decidido a cumplir con ese propósito, así que creo que no hay algo tan ilustrativo para mostrar los cambios en general como el que hablemos acerca del “nuevo” ASP.NET 4.0

Según wikipedia, ASP.NET tiene ya casi 8 años de haber sido released, sin embargo si leemos su historia, fue ideado y pensado para la arquitectura o forma de desarrollar aplicaciones web a finales de los años 90. En ese tiempo pensar en “compatiblidades” entre browsers era impensable (los niveles de compatibilidad entre los browsers realmente eran mucho más complicados de lo que son ahora). Programar en “client side” con javascript en el browser era un sueño lejano, trabajar con hojas de estilo era algo que (aún hoy para muchos) representa un tabú. Cuando vemos cosas como un gigantesco ViewState, un horrible markup generado sin estilos y usando tablas, impronunciables identificadores de elementos en markup de html, realmente estamos viendo la crema y nata del web development a finales de los años 90. Ya era hora de un cambio en el mundo de ASP.NET.

Hoy por hoy el uso de Javascript frameworks ya representa un “must have” en todo desarrollo web, HTML5 esta a la vuelta de la esquina, aplicaciones web netamente clientside ya son muy comunes, los paradigmas de desarrollo han cambiado drásticamente. Microsoft ha tratado de “ponerse al día” con estas nuevas tendencias, tomemos por ejemplo ASP.NET AJAX (yep, algo horrible y poco práctica al final, pero creo que fue el primer inicio de Microsoft en la dirección correcta) y hoy por hoy el nacimiento de ASP.NET MVC, casi que podríamos decir: “Runtime nuevo, ASP.NET nuevo”.

En esta serie trataré de dar un par de hojeadas a las cosas “nuevas” que nos ofrece ASP.NET 4.0, qué problemas resuelve y como cambia con respecto a versiones anteriores.

Hasta la próxima entrega :)

Lenguajes funcionales, divide y vencerás

Hace unos días me encontraba en una sesión de review de código en la oficina de un cliente y me topé con el típico problema recurrente del cual creo que ya muchos estamos empapados: “La clase superhéroe”, si, esa amiga que no sólo se conecta a la base de datos, sino que también crea la consulta, chequea los datos, genera el id aleatorio del usuario y también al mismo tiempo inserta y cambia los registros en la base de datos, todo en una “compacta” clase que hace literalmente todo.

No, este post no es acerca de SRP (Single Responsability Principle) o de principios de diseño y pensamiento en objetos ni tampoco se trata de la vieja escuela de “modularización” usando subrutinas (me imagino que alguien recuerda sus viejas lecturas de hace muchos años cuando aprendían algo como Pascal o BASIC). En vez de eso, decidí conversar un poco de lo que he aprendido mientras continuo mi jornada de aprendizaje en lenguajes funcionales (usando Haskell).

Advertencia: Estoy en el proceso de aprendizaje de lenguajes funcionales y Haskell, es muy probable que lo que exprese o diga en este post realmente sean producto de mi alta taza de ignorancia al respecto. :D

En Haskell las funciones son ciudadanos de primer orden, son uno más, de hecho, son sumamente importantes (me imagino que en los demás lenguajes funcionales el principio es el mismo). El principio que rige o elimina la duplicidad y la reutilización de operaciones en lenguajes funcionales es la simple composición de funciones, o sea, de forma similar a como deberíamos desarrollar aplicaciones mediante composición de objetos, en un lenguaje funcional, funciones complejas se crean mediante funciones más simples, primitivas y sencillas. Lección a aprender, procura que tu “método” o “función” (o como quieras llamarlo, si quieres, hasta “subrutina” le puedes poner) haga una y sólo una cosa.

Tomemos el siguiente ejemplo (tomado del libro Programming in Haskell de Graham Hutton) dónde definimos un cifrador de cesar usando simples funciones en Haskell (probablemente se puede definir en una forma mucho más óptima, pero que se yo!)

import Data.Char

{-
- Caesar cypher definition
-}

-- Transform a char to its numeric representation
let2int  :: Char -> Int
let2int a = ord a - ord 'a'

-- Transform an integer to a char representation
int2let  :: Int -> Char
int2let a = chr (ord 'a' + a)

-- Shift a character by its factor
shift    :: Int -> Char -> Char
shift n a | isLower a = int2let z
          | otherwise = a
    where
        x = let2int a
        y = x + n
        z = y `mod` 26 -- because we can reach z someday!

-- Now it is time to encrypt!
encode      :: Int -> String -> String
encode n xs = [shift n x | x <- xs]

La forma en que funciona es simple, al final esta declarada la función encode que utiliza a su vez list comprehension (hablaré de esto después) y a la función definida shif, que a su vez utiliza la función let2int e int2let que también usan a la función incluída ord (que existe en el space Data.Char).

Quizás todo esto se pueda ver “trivial” pero es muy común ver métodos o “Modules” con métodos en Visual Basic que literalmente hacen todo: piden la entrada a la pantalla al usuario, verifican que la entrada sea la correcta, luego hacen el calculo y por último le muestran al usuario el resultado, si, todo en el mismo Main del módulo…

Creo que debemos comenzar a aprender un poco más de lenguajes simples como los lenguajes funcionales, sin construcciones exóticas, sin extensiones mágicas… simples composiciones, simple reutilización, donde la simpleza es el motivo. Claro, esto no implica que no podemos hacer cosas “complejas” en un lenguaje funcional, por ejemplo, Darcs (un sistema de control de versiones distribuido) y Leksah (un IDE para Haskell) se encuentran ambos hechos en Haskell.

La próxima vez que se topen con un super gordo método main, recuerden que es mejor ponerlo a dieta, aprendan de Haskell :D

¡Saludos!

NOTA: mi amigo José Romaniello me comenta que el patrón o el “antipatrón” de la clase superhéroe se conoce como “God Class”, aunque yo prefiero el nombre de superhéroe :D

The complex way is not really the right way…

Today I was visiting a client, and I just stop and watch something that really caught my attention:

foreach (item in myList) {
    if (item.Length > 0) {
        SaveItem(item);
    }
}
// more ugly code...

Well, the code was really different (an uglier one) but you got the idea…. What I suggested to my client was a more concise solutio, create an extension method:

public static class IEnumerableExt {
    public static IEnumerable<T> ForEach(this IEnumerable<T> items, Action<T> action) {
        foreach(var item in items) {
            action(e);
        }
    }
}

The second part is the easy one…

myList
    .Where((item) => item.Length > 0)
    .ForEach((item) => SaveItem(Item));

I don’t know you, but this way looks a lot clearer to me…

Lessson learned: If your code looks complex, please, take a time and look how to make it more clearer.

Es bastante común toparnos con casos en los que debemos asegurarnos que el mismo test se cumpla para varias condiciones. Imagínense el desarrollo de una calculadora o mucho mejor, el de alguna pieza de lógica de negocio dependiente de data. En vez de escribir ciclos dentro de nuestro test o peor aún, escribir varios test para el caso, en XUnit.NET tenemos el apoyo de las “Theory” que son test que estan fuertemente ligados a los datos.

Los datos son pasados como parámetros del test y el origen de los datos depende de nuestro proveedor de datos seleccionado. En XUnit.NET Extensions tenemos de “cajita” varios proveedores de datos que podemos usar:

  • InlineDataAttribute, los datos se presentan en línea en cada atributo
  • ClassDataAttribute, los datos son proporcionados por una clase que implementa IEnumerable<T>
  • PropertyDataAttribute, los datos son entregados por la propiedad de una clase que retorna IEnumerable<T>
  • SqlServerDataAttribute, los datos vienen de una consulta de una base de datos de SQL Server
  • OleDbDataAttribute, los datos tienen como origen una consulta de un proveedor OleDb
  • ExcelDataAttribute, los datos tienen como origen una hoja de cálculo de Excel.

El uso de Theory y DataAttribute proporciona una agilidad increíble al probar ciertas condiciones que antes requeririan mucha más pericia. Depende de nosotros sacarle provecho :)

[Theory]
[InlineData(1, 2, 3)]
[InlineData(4, 5, 9)]
public void It_can_use_correctly_the_data_attribute(int a, int b, int result)
{
    Assert.Equal(result, (a + b));
}

[Theory, ExcelData("myTestData.xls", "SELECT a, b, result FROM Data")]
public void It_can_use_correctly_the_data_from_an_excel_file(int a, int b, int result)
{
    Assert.Equal(result, (a + b));
}

Bien, hasta aquí llegamos con las Extensiones de XUnit.NET, la próxima entrega veremos como extender a nuestro antojo XUnit.NET ;)