2010-12-10 25 views
25

¿Cómo puedo probar la unidad de Javascript que se basa en un lienzo HTML? Dibujar en el lienzo debe ser verificado.HTML Canvas Unit testing

+0

Podría ampliar su pregunta un poco? Exactamente, ¿qué quieres probar? Probablemente tengas que escribir alguna forma de funciones de validación para facilitar las pruebas. –

+0

Quiero probar un motor de dibujo de carta de Javascript. Quiero comprobar si las líneas y formas adecuadas se colocan en el lienzo HTML en los lugares correctos. (coordenadas, color, grosor, etc.) – pcjuzer

+3

Ok. Supongo que podrías mejorar algo a través de http://dev.w3.org/html5/2dcontext/#dom-context-2d-getimagedata. Puede usar ese método para verificar el color del píxel. Podría ser útil si pudieras escribir algunas pruebas falsas solo para descubrir qué tipo de API vas a necesitar. –

Respuesta

7

Como se discutió en los comentarios de la pregunta, es importante verificar que ciertas funciones se hayan invocado con los parámetros adecuados. pcjuzer propuso el uso del patrón de proxy. El siguiente ejemplo (código RightJS) muestra una manera de hacer esto:

var Context = new Class({ 
    initialize: function($canvasElem) { 
     this._ctx = $canvasElem._.getContext('2d'); 

     this._calls = []; // names/args of recorded calls 

     this._initMethods(); 
    }, 
    _initMethods: function() { 
     // define methods to test here 
     // no way to introspect so we have to do some extra work :(
     var methods = { 
      fill: function() { 
       this._ctx.fill(); 
      }, 
      lineTo: function(x, y) { 
       this._ctx.lineTo(x, y); 
      }, 
      moveTo: function(x, y) { 
       this._ctx.moveTo(x, y); 
      }, 
      stroke: function() { 
       this._ctx.stroke(); 
      } 
      // and so on 
     }; 

     // attach methods to the class itself 
     var scope = this; 
     var addMethod = function(name, method) { 
      scope[methodName] = function() { 
       scope.record(name, arguments); 

       method.apply(scope, arguments); 
      }; 
     } 

     for(var methodName in methods) { 
      var method = methods[methodName]; 

      addMethod(methodName, method); 
     } 
    }, 
    assign: function(k, v) { 
     this._ctx[k] = v; 
    }, 
    record: function(methodName, args) { 
     this._calls.push({name: methodName, args: args}); 
    }, 
    getCalls: function() { 
     return this._calls; 
    } 
    // TODO: expand API as needed 
}); 

// Usage 
var ctx = new Context($('myCanvas')); 

ctx.moveTo(34, 54); 
ctx.lineTo(63, 12); 

ctx.assign('strokeStyle', "#FF00FF"); 
ctx.stroke(); 

var calls = ctx.getCalls(); 

console.log(calls); 

se puede encontrar una demostración funcional here.

He utilizado un patrón similar para implementar algunas características que faltan en la API. Es posible que tengas que hackearlo un poco para que se ajuste a tus propósitos. ¡Buena suerte!

+0

Heh, te vi publicar esto en twitter y me preguntaba por qué habías escrito algo como esto :) –

-1

Dado que las "formas" y "líneas" dibujadas en un lienzo no son objetos reales (es como tinta sobre papel), sería muy difícil (¿imposible?) Hacer una prueba de unidad normal sobre eso.

Lo mejor que puede hacer con un lienzo estándar es analizar los datos de píxeles (de putImageData/getImageData. Al igual que lo que decía bedraw).

Ahora, no he intentado esto todavía, pero podría ser más de lo que necesita. Cake es una biblioteca para el lienzo. Está usando una gran cantidad de putImageData/getImageData. This example podría ayudar con lo que está tratando de hacer con una prueba.

Espero que ayude a responder su pregunta.

+0

Tan tedioso como es, uno puede escribir un objeto de Contexto simulado para que el código de dibujo dependa y pruebe que las llamadas apropiadas se realizan al objeto Contexto. – mlibby

15

Escribí un ejemplo para el lienzo de pruebas unitarias y otros tipos de imágenes con Jasmine y js-imagediff.

Jasmine Canvas Unit Testing

me parece que esto es mejor que asegurarse de que los métodos específicos en un lienzo simulacro se han invocado ya diferentes series de métodos puede producir el mismo método. Normalmente, crearé un lienzo con el valor esperado o utilizaré una versión estable del código para probar una versión de desarrollo.

0

He estado buscando pruebas de lienzos recientemente y ahora he pensado en una página que permite comparar el lienzo con una versión de imagen "bien conocida" de lo que debería ser el lienzo. Esto haría una comparación visual rápida y fácil.

Y tal vez tenga un botón que, suponiendo que la salida sea correcta, actualice la versión de la imagen en el servidor (enviando la salida toDataUrl() a la misma). Esta nueva versión se puede usar para futuras comparaciones.

No exactamente (en absoluto) automatizado, pero sí facilita la comparación de la salida de su código.

Editar:

Ahora he hecho esto:

Utility to test canvas output

El gráfico de la izquierda es el verdadero lienzo, mientras que la derecha es una imagen almacenada en una base de datos de lo que debe ser Me gusta (tomado de cuando sé que el código está funcionando). Habrá muchos de estos para probar todos (eventualmente) aspectos de mi código.

0

Desde el punto de vista de un desarrollador, el lienzo es casi solo de escritura, porque una vez dibujado es difícil recuperar algo útil de forma programática.Claro que se puede hacer un reconocimiento punto por punto, pero eso es demasiado tedioso y es difícil escribir y mantener esas pruebas.

Es mejor interceptar las llamadas realizadas a un objeto canvas e investigarlas. Aquí hay algunas opciones:

  1. Cree un objeto envoltorio que registre todas las llamadas. Juho Vepsäläinen publicó un ejemplo de este tipo.
  2. Si es posible, use una biblioteca como frabric.js que ofrezca un mayor nivel de abstracción para el dibujo. Los "dibujos" son objetos JS que se pueden inspeccionar directamente o convertir a SVG, que es más fácil de inspeccionar y probar.
  3. Utilice Canteen para interceptar todas las llamadas a funciones y cambios de atributos de un objeto canvas. Esto es similar con la opción 1.
  4. Use Canteen con rabbit, que le ofrece algunos adaptadores personalizados de tamaño y alineación Jasmine, y una función getBox() que se puede usar para determinar el tamaño y la posición del material dibujado en el lona.
1

Realizo lienzos muy simples y los pruebo con mocha. Lo hago de manera similar a Juho Vepsäläinen pero el mío se ve un poco más simple. Lo escribí en ec2015. clase

CanvasMock: clase

import ContextMock from './ContextMock.js' 

export default class { 
    constructor (width, height) 
    { 
    this.mock = []; 
    this.width = width; 
    this.height = height; 
    this.context = new ContextMock(this.mock); 
    } 

    getContext (string) 
    { 
    this.mock.push('[getContext ' + string + ']') 
    return this.context 
    } 
} 

ContextMock:

export default class { 
    constructor(mock) 
    { 
    this.mock = mock 
    } 

    beginPath() 
    { 
    this.mock.push('[beginPath]') 
    } 

    moveTo(x, y) 
    { 
    this.mock.push('[moveTo ' + x + ', ' + y + ']') 
    } 

    lineTo(x, y) 
    { 
    this.mock.push('[lineTo ' + x + ', ' + y + ']') 
    } 

    stroke() 
    { 
    this.mock.push('[stroke]') 
    } 
} 

algunas pruebas moca que evalúa la funcionalidad de la maqueta en sí:

describe('CanvasMock and ContextMock',()=> { 
    it('should be able to return width and height',()=> { 
     let canvas = new CanvasMock(500,600) 
     assert.equal(canvas.width, 500) 
     assert.equal(canvas.height, 600) 
    }) 
    it('should be able to update mock for getContext',()=> { 
     let canvas = new CanvasMock(500,600) 
     let ctx = canvas.getContext('2d') 
     assert.equal(canvas.mock, '[getContext 2d]') 
    }) 
}) 

A pruebas moca que evalúa la funcionalidad de una función que devuelve un lienzo:

import Myfunction from 'MyFunction.js' 

describe('MyFuntion',()=> { 
it('should be able to return correct canvas',()=> { 
    let testCanvas = new CanvasMock(500,600) 
    let ctx = testCanvas.getContext('2d') 
    ctx.beginPath() 
    ctx.moveTo(0,0) 
    ctx.lineTo(8,8) 
    ctx.stroke() 
    assert.deepEqual(MyFunction(new CanvasMock(500,600), 8, 8), canvas.mock, [ '[getContext 2d]', '[beginPath]', '[moveTo 0, 0]', [lineTo 8, 8]', '[stroke]' ]) 
}) 

por lo que en este ejemplo mifuncion toma el lienzo que pasa como un argumento (Myfunction (nueva CanvasMock (500.600), 8, 8)) y escribe una línea en él desde 0,0 a lo que sea que pase en como los argumentos (Myfunction (new CanvasMock (500,600), ** 8, 8 **)) y luego devuelve el lienzo editado.

por lo tanto, cuando utiliza la función en la vida real, puede pasar un lienzo real, no un lienzo falso y luego ejecutará los mismos métodos pero hará cosas reales de lienzo.

lectura acerca de burla here