jueves, 30 de agosto de 2012

Haciendo test en javascript con jasmine

Antes de irme de vacaciones asistí a unas charlas denominadas #jsweek dadas por el grupo @agileCanarias. La charlas fueron impartidas por @ydarias y por @axelhzf, y se tocaron temas tan divertidos como los WTF de javascript, CoffeeScript y Backbone.

Bien, en la charla de introducción a javascript aprendí a usar jasmine, que no es otra cosa que un framework para hacer test unitarios en javascript. A medida que escribíamos los test para la kata fizzbuzz me di cuenta que podría aplicar este framework a mi método de comparación de objetos que expliqué en mi anterior post (Comparando objetos en javascript)

Sin ser un experto, o más bien, considerándome un neófito en esto de los test unitarios, lo primero que debemos hacer es bajarnos el framework desde su página web. Una vez descargado y descomprimido veremos que tenemos la siguiente estructura
  • Un fichero SpecRunner.html desde donde se ejecutaran los test que definamos.
  • Una carpeta lib donde esta el propio framework y donde en principio no tenemos que tocar nada.
  • Una carpeta spec donde irán los test que queremos ejecutar.
  • Una carpeta src donde irá el código que queremos probrar.
En mi caso he creado un proyecto llamado HelperJS con un fichero prototypes.js donde está el código descrito en el post anterior y una carpeta test que tiene la estructura anteriormente descrita. Ahora lo que haremos será abrir el fichero SpectRunner.html y añadiremos las referencias a los ficheros de test y ded código que queremos probar.
<!-- include spec files here... -->
<script type="text/javascript" src="spec/prototypesSpec.js"></script>

<!-- include source files here... -->
<script type="text/javascript" src="../prototypes.js"></script>
Si abrimos la página SpecRunner.html en el navegador obtendremos una página vacia, por lo que lo siguiente será escribir los test en el fichero prototypesSpec.js. Siguiendo un poco la métodología TDD (y repito que soy un novato total en el tema) lo normal es escribir los test de lo más simple a lo más complejo (aunque en mi caso el código ya está escrito). Basándome en esto lo más simple es escribir un test para cuando el segundo objeto es null o undefined. Con esto podemos escribir nuestro primeros test de la siguiente manera
describe("Object.prototype.equals", function() {
 it("El objeto a comparar es null", function() {
  var u1 = {prop1: "1", prop2: 2};
  var u2 = null;

  expect(u1.equals(u2)).toBe(false);
 });

 it("El objeto a comparar es undefined", function() {
  var u1 = {prop1: "1", prop2: 2};
  var u2 = undefined;

  expect(u1.equals(u2)).toBe(false);
 });
});
Como vemos el código es bastante fácil de entender. El método describe se usar para definir un grupo de test (u otro grupo) y le pasamos una función donde se definen los test. Para definir un test usamos el método it, al que le pasamos una descripción del test y un función donde se ejecuta el test gracias a expect donde indicamos lo que queremos probar y el valor que debe tener. Si todo ha ido bien al recargar la página SpecRunner.html debemos obtener algo como esto


Con esto claro, ya podemos escribir el resto de test en lo que comprobamos distintas posibilidades que se pueden dar.
describe("Object.prototype.equals", function() {
 it("El objeto a comparar es null", function() {
  var u1 = {prop1: "1", prop2: 2};
  var u2 = null;

  expect(u1.equals(u2)).toBe(false);
 });

 it("El objeto a comparar es undefined", function() {
  var u1 = {prop1: "1", prop2: 2};
  var u2 = undefined;

  expect(u1.equals(u2)).toBe(false);
 });

 it("Distinto numero de propiedades", function() {
  var u1= {prop1 : "value1", prop2: 2};  
  var u2= {prop1 : "value1", prop2: 2, prop3: 3};  

  expect(u1.equals(u2)).toBe(false);
 });

 it("Mismas propiedades pero con distintos valores", function() {
  var u1= {prop1 : "value1", prop2: 2};  
  var u2= {prop1 : "value1", prop2: 3};  

  expect(u1.equals(u2)).toBe(false);
 }); 

 it("Mismo numero de propiedades pero con nombres distintos", function() {
  var u1= {prop1 : "value1", prop2: 2};  
  var u2= {prop1 : "value1", prop3: 3};  

  expect(u1.equals(u2)).toBe(false);
 }); 
  
 it("Dos objetos simples iguales", function() {
  var u1 = {prop1: "1", prop2: 2};
  var u2 = {prop1: "1", prop2: 2};

  expect(u1.equals(u2)).toBe(true);
 });

 it("Dos objetos compuestos distintos", function() {
  var u1 = {prop1: "1", prop2: 2, prop3: { prop4: 4}};
  var u2 = {prop1: "1", prop2: 2, prop3: { prop5: 5}};

  expect(u1.equals(u2)).toBe(false);
 });

 it("Dos objetos compuestos iguales", function() {
  var u1 = {prop1: "1", prop2: 2, prop3: { prop4: 4}};
  var u2 = {prop1: "1", prop2: 2, prop3: { prop4: 4}};

  expect(u1.equals(u2)).toBe(true);
 });
});
Obteniendo la siguiente salida


Como vemos es bastante fácil escribir test simples para nuestro código javascript. Si estás interesado en probar otros framework de este tipo también está QUnit que por lo que me han comentado también está bastante bien.

Todo esta código espero subirlo próximamente a GitHub.

Happy coding!

viernes, 10 de agosto de 2012

Comparando objetos en javascript

La verdad es que por muchas horas que invierta en programar en javascript y en leer esos pequeños detalles que tiene y que pueden volverte loco un par de horas no paro de sorprenderme al ver algunas cosas. La última sin ir más lejos es la siguiente y la mostraré con un simple trozo de código.
var u1 = {prop1 : 1, prop2: "value2"};
var u2 = {prop1 : 1, prop2: "value2"};
console.log(u1 == u2); 
console.log(u1 === u2);
Simplemente estamos declarando dos objetos y los estamos comparando. Como vemos ambos objetos tienes las mismas propiedades (prop1 y prop2) y los mismos valores en dichas propiedades, prop1 vale 1 y prop2 vale "value2". Las siguientes lineas muestran por la consola el resultado de comparar ambos objetos. Supongo que la respuesta lógica sería pensar que algunas de las dos comparaciones debería devolver true. ¿Verdad?, pues no, ambas comparaciones devuelve false.

Si el objeto lo declaramos de otra manera menos que el efecto es el mismo
function Foo(p1,p2) {
    this.prop1 = p1;
    this.prop2 = p2;    
}

var u1 = new Foo(1, "value2");
var u2 = new Foo(1, "value2");

console.log(u1 == u2);  // false
console.log(u1 === u2); // false
Vemos que en ese caso el comportamiento similar así que podemos descartar que este comportamiento sea debido a como hemos declarado nuestros objetos.

Después de pensar un poco en el asunto y necesitando una función que me comparara dos objetos busqué la forma de hacer dicha comparación. Lo primero que determiné es que son para mi dos objetos iguales, y para mi negocio son aquellos objetos cuyas propiedades (solo propiedades y no funciones) sean iguales. O sea que esta comparación sería correcta
function Foo(p1,p2) {
    this.prop1 = p1;
    this.prop2 = p2;
    this.bar = function() {
        // do operation
    }        
}

var u1 = new Foo(1, "value2");
var u2 = new Foo(1, "value2");

console.log(u1 == u2);  // false
console.log(u1 === u2); // false
Con eso claro lo único que tenemos que hacer es recorrer las propiedades de los objetos implicados e ir comparando valores. Recorrer las propiedades de un objeto es bastante fácil pero puede darnos algún problema si no tenemos algunas cosas en cuenta. En concreto el principal problema es que dependiendo del contexto donde recorramos las propiedades del objeto podemos obtener las propiedades heredadas de dicho objeto, por lo que para evitarlo debemos usar el método hasOwnProperty, que nos asegura que esa propiedad es del objeto en cuestión. En nuestro ejemplo
function Foo(p1,p2) {
    this.prop1 = p1;
    this.prop2 = p2;
    this.func1 = function() {
        // do operation
    }        
}

var u1 = new Foo(1, "value2");
for (var p in u1)
{
  if (u1.hasOwnProperty(p))
    console.log(p);        
}
Obtendríamos esta salida
prop1
prop2
bar
Con esto claro ya podemos intentar hacer nuestro comparador de objetos. La primera versión que se nos viene a la mente es hacer algo así
function equals(p1, p2)
{
    // Comparamos los objetos
}
Esta alternativa no es mala, pero pienso que es más elegante extender el tipo object y añadir el método equals, para poder hacer algo así
p1.equals(p2);
Esto es bastante fácil en javascript y el esqueleto sería el siguiente
Object.prototype.equals = function(x)
{
  // Comparamos los objetos 
}
Al principio del método podemos hacer algunas comparaciones básicas como si x no está definido o si los objetos a comparar tienen el mismo número de propiedades. Esta útlima comparación se hace a través del método keys que desgraciadamente no está disponible en todos los navegadores (se puede consultar su disponibilidad aquí). Con esto, nuestro método ya va tomando cuerpo y sería de la siguiente forma
Object.prototype.equals = function(x)
{   
    if (x === null || x === undefined) 
      return false;
            
    if (Object.keys(this).length != Object.keys(x).length)
      return false;
       
    // Comparamos los objetos                             

    return true;                
}
Con esto ya lo único que nos queda es recorrer las propiedades del objeto de origen e ir buscando sus homólogas para luego comparar su valor. En estas comparaciones veremos de que tipo es la propiedad, para ver si comparamos directamente sus valores, volvernos a llamar al método equals en caso de ser un objeto o devolvemos false en caso que sea una función. Con esto nuestro método equals quedaría de la siguiente manera
Object.prototype.equals = function(x)
{   
    if (x === null || x === undefined) 
        return false;
            
    if (Object.keys(this).length != Object.keys(x).length) return false;
                                   
    for (var p in this)
    {
        // Evitamos navegar por las propiedades "heredadas"
        if (this.hasOwnProperty(p)) {
            // No es una propiedad de x                 
            if (!x.hasOwnProperty(p)) return false;             
            switch(typeof(this[p])) {
                case 'function': 
                    // No admitimos objetos con funciones
                    return false;
                case 'object': 
                    // Comparamos los objetos
                    if (!this[p].equals(x[p]))
                         return false;  
                    break; 
                default:             
                    // Las propiedades tienes valores distintos
                    if (this[p] !== x[p])
                        return false;
                    break;
            }                        
        }
    }

    return true;                
}
Para probar este código de manera rudimentaria he hecho este código y lo he lanzado en jsFiddle y los resultado obtenidos son los esperados
var u1= {prop1 : "value1", prop2: "value2"};
var u2= {prop1 : "value1", prop2: "value2"};
console.log('u1 equals u2: ' + u1.equals(u2));

var v1= {prop1 : "value1", prop2: "value2"};
var v2= {prop1 : "value1", prop2: "value2", prop3: "value3"};
console.log('v1 equals v2: ' + v1.equals(v2));    

var w1= {prop1 : "value1", prop2: "value2"};
var w2= {prop1 : "value1", prop2: "value3"};
console.log('w1 equals w2: ' + w1.equals(w2)); 

var x1= {prop1 : "value1", prop2: "value2"};
var x2= {prop1 : "value1", prop3: "value2"};
console.log('x1 equals x2: ' + x1.equals(x2));

var y1= {prop1 : "value1", prop2: { prop3: "value3"}};
var y2= {prop1 : "value1", prop2: { prop4: "value4"}};
console.log('y1 equals y2: ' + y1.equals(y2));

var z1= {prop1 : "value1", prop2: { prop3: "value3"}};
var z2= {prop1 : "value1", prop2: { prop3: "value3"}};
console.log('z1 equals z2: ' + z1.equals(z2));
Puedes probar este código desde aquí

Se que esta no es la manera más óptima de probar el código y para esto existen framework de javascript como Jasmine o QUnit pero esta parte la dejaré para otro post.

En definitiva hemos visto que hay que estar atentos a muchos comportamientos que tiene javascript ya que algo tan simple como una comparación de objetos puede ser una vez más una tarea complicada.

Happy coding!

martes, 7 de agosto de 2012

Los operadores de comparación en javascript

Desde hace algún tiempo cada vez invierto más horas de mi jornada laboral en programar en javascript. A medida que vas programando en este lenguaje te vas dando cuenta de las muchas peculiaridades que tiene, y que te puede arruinar una jornada laboral.

Para los que vienen de programar en lenguajes como C# este tipo de sentencias son de lo más normales
if (a == b) { 
  // a y b son iguales
}
El problema está en que javascript las cosas no son como nosotros estamos acostumbrados que sean y este comparador nos pueden dar algún que otro quebradero de cabeza y más teniendo en cuenta que también tenemos un triple comparador, si como lo oyes, tenemos un triple comparador que se escribe ===.

Entonces, ¿cuál es la diferencia entre == y ===? Pues bien, el doble comparador por decirlo de una manera hace una comparación por valor y en caso que los tipos no sean igual javascript hace una coerción de tipos. El triple comparador, al contrario que el doble comparador, por decirlo de alguna manera fuerza la igualdad de tipos y del valor que estamos comparando.

Sabiendo esto, tenemos que estar atento a la hora de comparar variables porque javascript puede resolver la comparación de la manera que nosotros no esperamos.

Algunos ejemplos para que todo quede más claro
1 == 1            // true como era de esperar
1 == "1"          // true, se convierte la cadena de texto a numero
null == undefined // true
1 == true         // true
"" == false       // true
'\n' == false     // true, el '\n' se interpreta como la cadena vacía
"" == 0           // true
'\n' == 0         // true
Como vemos en todas las comparaciones (salvo la primera) esperaríamos que javascript nos devolviera false, sin embargo nos devuelve true. Para salvar esto deberíamos usar el triple comparador.
1 === 1            // sigue siendo true
1 === "1"          // false
null === undefined // false
1 === true         // false
"" === false       // false
'\n' === false     // false
"" === 0           // false
'\n' === 0         // false
De igual forma que tenemos == y ===, también tenemos != y !== cuyo comportamiento es análogo.

Otras comparaciones curiosas pero menos comunes son
NaN == NaN  // false, NaN es Not a Number y no se puede comparar
NaN === NaN // false
1.0 == 1    // true
1.0 === 1   // true, para javascript ambos son de tipo numeric
La coerción de tipos puede ser algo realmente frustrante y que nos puede volver locos un par horas, pero que nos puede venir bastante bien en algunas situaciones por lo que lo mejor es estar seguro de lo que estamos haciendo en cada momento.

Happy coding!

lunes, 6 de agosto de 2012

Introducción a Knockout (VIII) - obsersable computed y binding event

Con los binding que trae knockout de fábrica debería ser más que suficiente para la mayoría de los proyectos en los que usemos knockout. Pese a ésto knockout nos brinda la forma de poder extender estos comportamientos predefinidos para poder tener un control total sobre nuestra vista y así no tener prácticamente ninguna limitación.

Computed Observables
Dentro de los observables que nos da knockout tenemos los computed observables (prefiero no traducirlo). Son funciones, que depende de uno o más observables y que se actualizan automáticamente cuando una de sus dependencias cambia. Típicamente se usan para información que se muestra en solo lectura aunque como veremos más adelante también los computed observables pueden ser de escritura y ellos a su vez puede actualizar valores.

Supongamos esta vista
<p>Nombre: <input type="text" data-bind="value: firstname" /></p>
<p>Apellidos: <input type="text" data-bind="value: lastname" /></p>
<p>Nombre completo: <label data-bind="text: name"></p>
Y este modelo
function ViewModel(model) {  
    var self = this;  
  
    self.firstname = ko.observable('');  
    self.lastname = ko.observable('');  
    self.name = ko.computed(function() {
        if (self.firstname() == '' || self.lastname() == '') return '';
         return self.lastname() + ', ' + self.firstname();
    });
}  

var viewModel = new ViewModel();  
ko.applyBindings(viewModel);​
Como vemos establecemos dos observables sobre firstname y lastname. También definimos observable computed que actualiza la propiedad name. Esta función se lanzará cada vez que la propiedades lastname y firstname cambien. Lo realmente maravilloso es que la función detecta a que propiedades debe detectar los cambios sin que nosotros debamos hacer nada más que usarlas en el cuerpo de la función.

Podemos probar este ejemplo en el siguiente enlace.

Como comentamos antes, los computed observables también pueden ser de escritura y ellos actualizar determinados valores. He de reconocer que esto no suele ser muy común pero puede que en algún momento nos sea útil. Para hacer un computed observables de lectura y escritura en el ejemplo anterior cambiaremos la vista
<p>Nombre: <input type="text" data-bind="value: firstname" /></p>
<p>Apellidos: <input type="text" data-bind="value: lastname" /></p>
<p>Nombre completo: <input type="text" data-bind="value: name"></p>
y el modelo de la siguiente manera
function ViewModel(model) {  
    var self = this;  
  
    self.firstname = ko.observable('');  
    self.lastname = ko.observable('');  
    self.name = ko.computed({
        read: function() {
            if (self.firstname() == '' || self.lastname() == '') return '';
                return self.lastname() + ', ' + self.firstname();
        },
        write: function(value) {
            var index = value.lastIndexOf(", ");
            if (index > 0) 
            { 
                self.firstname(value.substring(index + 2));
                self.lastname(value.substring(0, index));
            }
        }
    });
}  

var viewModel = new ViewModel();  
ko.applyBindings(viewModel);​
Tal y como hicimos antes, si actualizamos el nombre y los apellidos automáticamente se actualizará el campo del nombre del completo. La salvedad en este caso, es que si actualizamos el nombre completo nosotros con el formato apellidos, nombre, se actualizarán los campos nombre y apellidos.

Podemos probar este ejemplo en el siguiente enlace.

Binding Event
Otra de las posibilidades que nos da knockout es trabajar directamente con los eventos que tienes javascript, pese a que ya tenemos algunos bindings que trabajan con ellos como puede ser el binding click.
Por ejemplo, si definimos la siguiente vista
<div>
    <input type="text" data-bind="value: message, event: { keyup: onkeyup }" />
    <label data-bind="text: status" ></label>
</div>​
y el siguiente modelo
function ViewModel(model) {  
    var self = this; 

    self.message = ko.observable('');
    self.status = ko.observable('Has escrito 0 caracteres...');
  
    self.onkeyup = function(data, event) {
        self.status('Has escrito ' + event.target.value.length + ' caracteres...');           
        return true;
    }
}  
ko.applyBindings(new ViewModel());
En este ejemplo estamos enganchándonos al evento keyup de un textbox y actualizamos un label con la información del número de caracteres que hemos introducido en el textbox.

Podemos probar este ejemplo en el siguiente enlace.

Como vemos, knoutout es bastante versátil y prácticamente podemos hacer lo que queramos. En el próximo artículo veremos como hacer nuestros propios bindings que también nos pueden ser útiles en algunos contextos.

Happy coding!