├── cover.png ├── leanpub ├── highlighting-ch05.txt ├── highlighting-ch06.txt ├── highlighting-ch04.txt ├── highlighting-ch09.txt ├── STEPS ├── get_sources.pl └── convert.pl ├── cc-by-sa.png ├── cc-by-sa-big.png ├── .gitignore ├── README.md ├── cover.html ├── fix-epub-toc.py ├── single-html-listing-title-hack.pl ├── add-epub-cover.sh ├── documentacion_activa-biblio.asc ├── set_pdf_metadata.bash ├── calidad-biblio.asc ├── chunked-html-listing-title-hack.pl ├── tdd-biblio.asc ├── integracion_continua-biblio.asc ├── consejospruebas-biblio.asc ├── funcionales-biblio.asc ├── fix-highlighting.py ├── insert_snippets.bash ├── tex-listing-title-hack.pl ├── extras ├── eclipse.asciidoc └── herramientas.asciidoc ├── Makefile ├── libro.css ├── book.asc.in ├── theme-libro └── libro.css ├── documentacion_activa.asc.in ├── calidad.asc ├── integracion_continua.asc ├── documentacion_activa.asc ├── funcionales.asc ├── tdd.asc.in └── consejospruebas.asc /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emanchado/camino-mejor-programador/HEAD/cover.png -------------------------------------------------------------------------------- /leanpub/highlighting-ch05.txt: -------------------------------------------------------------------------------- 1 | java 2 | html 3 | java 4 | java 5 | java 6 | java 7 | 8 | -------------------------------------------------------------------------------- /leanpub/highlighting-ch06.txt: -------------------------------------------------------------------------------- 1 | ruby 2 | ruby 3 | ruby 4 | python 5 | python 6 | js 7 | -------------------------------------------------------------------------------- /cc-by-sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emanchado/camino-mejor-programador/HEAD/cc-by-sa.png -------------------------------------------------------------------------------- /cc-by-sa-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emanchado/camino-mejor-programador/HEAD/cc-by-sa-big.png -------------------------------------------------------------------------------- /leanpub/highlighting-ch04.txt: -------------------------------------------------------------------------------- 1 | cl 2 | cl 3 | cl 4 | python 5 | python 6 | ruby 7 | javascript 8 | ruby 9 | javascript 10 | haskell 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.swp 3 | cover.pdf 4 | book.asc 5 | book.tex 6 | camino_*.pdf 7 | camino_*.html 8 | camino_*.epub 9 | camino_*.chunked 10 | camino_*.xml 11 | tex-tmp 12 | tmp 13 | epub-tmp 14 | .idea/ 15 | -------------------------------------------------------------------------------- /leanpub/highlighting-ch09.txt: -------------------------------------------------------------------------------- 1 | scala 2 | scala 3 | scala 4 | scala 5 | scala 6 | scala 7 | scala 8 | scala 9 | scala 10 | scala 11 | scala 12 | scala 13 | scala 14 | scala 15 | scala 16 | scala 17 | scala 18 | scala 19 | scala 20 | scala 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | El camino a un mejor programador 2 | ================================ 3 | 4 | Código fuente (en AsciiDoc) del libro «El camino a un mejor 5 | programador», una colección de artículos técnicos sobre ingeniería de 6 | software de Joaquín Caraballo Moreno, Yeray Darias Camacho y Esteban 7 | Manchado Velázquez. 8 | 9 | Más información en http://emanchado.github.com/camino-mejor-programador 10 | -------------------------------------------------------------------------------- /cover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
10 | ;
15 | $title =~ s|\...||;
16 | $title =~ s|<(/?)strong>|<$1em>|g;
17 |
/) {
14 | if (--$number == 0) {
15 | $state = "print";
16 | }
17 | }
18 |
19 | if ($state eq "print") {
20 | last if $line =~ /^<\/tt><\/pre>/;
21 |
22 | $line =~ s/<[^>]+>//go;
23 | $line =~ s/<//go;
25 | $line =~ s/"/\"/go;
26 | $line =~ s/&/&/go;
27 | print $line;
28 | }
29 | }
30 | close F;
31 |
--------------------------------------------------------------------------------
/integracion_continua-biblio.asc:
--------------------------------------------------------------------------------
1 | [bibliography]
2 | .Integración continua
3 | - [[[continuosintegration]]] Paul M Duvall. «Continuos Integration: Improving
4 | Software Quality and Reducing Risk». Addison-Wesley Professional. ISBN 978-0321336385
5 | - [[[continuosdelivery]]] Jez Humble y David Farley. «Continuos Delivery».
6 | Addison-Wesley Professional. ISBN 978-0321601919
7 | - [[[fowlerci-bis]]] Martin Fowler. «Continuous Integration».
8 | http://www.martinfowler.com/articles/continuousIntegration.html
9 | - [[[xpexplained]]] Kent Beck y Cynthia Andres. «Extreme Programming Explained:
10 | Embrace Change». Addison-Wesley Professional. ISBN 978-0321278654
11 | - [[[bleyco]]] Carlos Blé Jurado. «Diseño ágil con TDD». ISBN 978-1445264714. http://dirigidoportests.com/el-libro
12 | - [[[xunit]]] Wikipedia. «Frameworks xUnit». http://en.wikipedia.org/wiki/XUnit
13 |
--------------------------------------------------------------------------------
/consejospruebas-biblio.asc:
--------------------------------------------------------------------------------
1 | [bibliography]
2 | .Siete problemas al probar programas
3 | - [[[pseudocode]]] Esteban Manchado Velázquez. «From pseudo-code to code».
4 | http://hcoder.org/2010/08/10/from-pseudo-code-to-code/
5 | - [[[testing123]]] Esteban Manchado Velázquez. «Software automated testing 123».
6 | http://www.demiurgo.org/charlas/testing-123/
7 | - [[[fowlerci]]] Martin Fowler. «Continuous Integration».
8 | http://www.martinfowler.com/articles/continuousIntegration.html
9 | - [[[kivaapi]]] API pública de Kiva. http://build.kiva.org/docs/
10 | - [[[loanmeter]]] Esteban Manchado Velázquez. «World Loanmeter».
11 | https://github.com/emanchado/world-loanmeter
12 | - [[[jasmine]]] Página web de Jasmine, un módulo de pruebas para Javascript.
13 | http://pivotal.github.com/jasmine/
14 | - [[[pydoubles]]] PyDoubles Test doubles framework. http://www.pydoubles.org/
15 |
--------------------------------------------------------------------------------
/funcionales-biblio.asc:
--------------------------------------------------------------------------------
1 | [bibliography]
2 | .Lecciones de aprender un lenguaje funcional
3 | - [[[onlisp]]] Paul Graham. «On Lisp». Prentice Hall. ISBN 0130305529.
4 | http://www.paulgraham.com/onlisp.html
5 | - [[[landoflisp]]] Conrad Barski. «Land of Lisp». No Starch Press. ISBN
6 | 978-1-59327-281-4. http://landoflisp.com/
7 | - [[[learnhaskell]]] Miran Lipovača. «Learn You a Haskell for Great Good!». No
8 | Starch Press. ISBN 978-1-59327-283-8. http://learnyouahaskell.com/
9 | - [[[progscala]]] Dean Wampler y Alex Payne. «Programming Scala». O'Reilly
10 | Media. ISBN 978-0-596-15595-7. http://ofps.oreilly.com/titles/9780596155957/
11 | - [[[proginscala]]] Martin Odersky, Lex Spoon, y Bill Venners. «Programming in
12 | Scala». Artima. ISBN 9780981531601. http://www.artima.com/pins1ed/
13 | - [[[jsfuncional]]] Dmitry A. Soshnikov. «JavaScript array "extras" in detail».
14 | http://dev.opera.com/articles/view/javascript-array-extras-in-detail/
15 |
--------------------------------------------------------------------------------
/fix-highlighting.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import sys
4 | import re
5 | from subprocess import Popen, PIPE
6 | import HTMLParser
7 |
8 | file = sys.argv[1]
9 | lang_sig_re = re.compile(r'\s*LANGUAGE=(\S+) ')
10 | end_lang_sig_re = re.compile(r'
$')
11 |
12 | f = open(file)
13 | state = 'init'
14 | code_html = ""
15 | lang = ""
16 | for line in f:
17 | m = lang_sig_re.search(line)
18 | if m:
19 | state = 'programlisting'
20 | lang = m.group(1)
21 | code_html = re.sub(lang_sig_re, '', line)
22 | elif state == 'programlisting':
23 | code_html = code_html + line
24 |
25 | if state == 'programlisting' and end_lang_sig_re.search(line):
26 | code_html = re.sub(end_lang_sig_re, '', code_html)
27 | line = ""
28 | state = 'init'
29 | p = Popen(["source-highlight", "-s", lang], stdin=PIPE, stdout=PIPE)
30 | code = HTMLParser.HTMLParser().unescape(unicode(code_html, "utf-8"))
31 | p.stdin.write(code.encode("utf-8"))
32 | p.stdin.close()
33 | print p.stdout.read().replace(') {
27 | open F2, $_;
28 | while (my $line = decode("utf-8", )) {
29 | # Drop the "_" from the line before trying to match: the
30 | # emphasis marks are dropped from the LaTeX version
31 | $line =~ s/_//g;
32 | if ($line =~ /^\.$title \((.*)\[.*\]\)/) {
33 | return $1;
34 | }
35 | }
36 | close F2;
37 | }
38 |
39 | return;
40 | }
41 |
42 | open F, $file;
43 | while (my $line = ) {
44 | if ($line =~ /{\\bf (.+)}/) {
45 | $title = decode("iso-8859-1", $1);
46 | ; # Discard the next line, should be empty
47 | } elsif ($title && $line =~ /^\\begin{lstlisting}/) {
48 | # Try to find the URL for this snippet. If found, add a link.
49 | my $url = find_snippet_url($title) // "";
50 | if ($url) {
51 | $title =~ /(.+) \((.+)\)/;
52 | my ($human_title, $filename) = ($1, $2);
53 | $line =~ s/,\]/,title={$human_title (\\href{$url}{$filename})}]/;
54 | } else {
55 | $line =~ s/,\]/,title={$title}]/;
56 | }
57 | print $line;
58 | $title = undef;
59 | } else {
60 | print $line;
61 | }
62 | }
63 | close F;
64 |
--------------------------------------------------------------------------------
/leanpub/convert.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | use strict;
4 | use warnings;
5 |
6 | use Encode;
7 | use File::Spec;
8 |
9 | my $help_string = "Need a source directory with the ch*.txt files, a destination directory\nand the directory with the HTML \"sources\"!\n";
10 | my $orig_dir = shift || die $help_string;
11 | my $dest_dir = shift || die $help_string;
12 | my $html_dir = shift || die $help_string;
13 |
14 | opendir D, $orig_dir or die "Can't open directory $orig_dir";
15 | while (my $file = readdir D) {
16 | next unless $file =~ /^(ch|bi)\d+\.txt$/;
17 |
18 | open F, File::Spec->catfile($orig_dir, $file);
19 | open F1, ">" . File::Spec->catfile($dest_dir, $file);
20 |
21 | # Tweak for syntax highlighting. Files like "highlighting-ch04.txt"
22 | # can be used to force a certain language for the syntax
23 | # highlighting (the first line is the character encoding for the
24 | # output, and each line after that is for one source code block, in
25 | # order).
26 | my @langs;
27 | open FLANGS, "highlighting-$file" and do {
28 | @langs = map { chomp; $_ } ;
29 | close FLANGS;
30 | };
31 |
32 | my $status = 'header';
33 | my $source_block_n = 0;
34 | while (my $line = ) {
35 | if ($status eq 'header') {
36 | if ($line =~ /^\* \* \*/) {
37 | $status = 'title';
38 | }
39 | }
40 | elsif ($status eq 'title') {
41 | if ($line =~ /^#/) {
42 | $line =~ s/\#//;
43 | $status = 'print';
44 | }
45 | print F1 $line;
46 | }
47 | elsif ($status eq 'print') {
48 | if ($line =~ /^\* \* \*/) {
49 | $status = 'footer';
50 | }
51 | else {
52 | if ($line =~ /^ $/) {
53 | while ($line = ) {
54 | if ($line =~ /^_/) {
55 | my $html_file = File::Spec->catfile($html_dir, $file);
56 | $html_file =~ s/\.txt/.html/o;
57 | $source_block_n++;
58 | my $source_code =
59 | decode('utf-8', `./get_sources.pl $html_file $source_block_n`);
60 | $source_code =~ s/^/ /gom;
61 | my $current_lang = $langs[$source_block_n - 1];
62 | if ($current_lang) {
63 | $source_code = qq({:lang="$current_lang"}) . "\n" .
64 | $source_code;
65 | }
66 | print F1 "\n", encode('utf-8', $source_code);
67 | last;
68 | }
69 | }
70 | }
71 |
72 | # Fix footnotes
73 | $line =~ s/\[\[(\d+)\]\(#ftn.idp\d+\)\]/[^$1]/g;
74 | # Fix bibliography references
75 | $line =~ s/bi01\.html/(#/g;
76 |
77 | # Add ids to bibliography entries
78 | if ($file =~ /^bi/) {
79 | $line =~ s/^\[([^\]]+)\]/{#$1}\n[$1]/;
80 | }
81 |
82 | print F1 $line;
83 | }
84 | }
85 | elsif ($status eq 'footer') {
86 | # We collect any footnotes there might be here
87 | if ($line =~ /^\[\[(\d+)\][^\]]+\] (.+)/) {
88 | my ($ft_number, $ft_text) = ($1, $2);
89 | # Fix bibliography references
90 | $ft_text =~ s/\(bi01\.html#/(#/g;
91 | print F1 "[^$ft_number]: $ft_text\n\n";
92 | }
93 | }
94 | }
95 | close F1;
96 | close F;
97 | }
98 | closedir D;
99 |
--------------------------------------------------------------------------------
/extras/eclipse.asciidoc:
--------------------------------------------------------------------------------
1 | Eclipse
2 | =======
3 | Yeray Darias Camacho
4 |
5 | Cuando se trabaja con Java, una de las herramientas más famosas y a tener muy en cuenta es Eclipse. Se trata de un entorno de desarrollo integrado (IDE) de código libre y gratuito, una de las razones por la que se ha hecho tan famoso.
6 |
7 | Como cualquier otro IDE tiene muchísimas funciones diferentes y soporta la edición de diferentes lenguajes de programación, aunque demuestra una madurez mucho mayor cuando se trabaja con Java. Debido a que utiliza un sistema de plug-ins para su extensión por terceras partes, siempre es conveniente que echemos un vistazo a las aportaciones de la comunidad, nunca se sabe que nuevas herramientas podemos utilizar.
8 |
9 | Crear un proyecto
10 | -----------------
11 |
12 | El primer paso será la creación del proyecto, en general siempre lo haremos desde el propio IDE, pero hay ocasiones en que el proyecto ha sido creado previamente y lo debemos importar.
13 |
14 | Desde el entorno
15 | ~~~~~~~~~~~~~~~~
16 |
17 | De serie, Eclipse permite crear una relativa variedad de proyectos Java, desde un proyecto de escritorio a una aplicación web dinámica, que se pueden ver incrementados en función de los plug-ins que tengamos instalados. Pero generalmente el proceso para crear un proyecto es siempre el mismo.
18 |
19 | File > New > Project
20 |
21 | De aquí en adelante cambia en función del tipo de proyecto, pero siempre estamos orientados por un asistente que nos indica que campos debemos rellenar, y como hacerlo. Se nos solicitará el nombre del proyecto, que carpetas de fuentes y de test queremos usar, ese tipo de detalles que solemos hacer manualmente mediante la creación de ficheros y directorios en una consola.
22 |
23 | Desde el sistema de fichero
24 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 |
26 | Es muy típico en los últimos años encontrarnos con sistemas de construcción como puede ser Maven o sistemas anteriores como Ant, lo que permite trabajar en consola de forma sencilla. Pero cuando los proyectos crecen es más sencillo seguir trabajando en Eclipse, lo cual es posible mediante la opción de importación.
27 |
28 | File > Import
29 |
30 | El tipo de importación que queramos hacer dependerá de si es un proyecto Maven, es un proyecto que se ha creado anteriormente en otro IDE, o tiene alguna otra estructura específica. Obviamente Eclipse no hace magia y si no disponemos de un plug-in que sepa trabajar con la estructura del fichero, no ganaremos mucho trabajando con Eclipse.
31 |
32 | Atajos de teclado
33 | -----------------
34 |
35 | Para moverse entre distintos paneles o ficheros
36 |
37 | [width="80%",options="header"]
38 | |======================
39 | |Atajo de teclado |Significado
40 | |+Shift-Cmd-R+ |Abrir un fichero (recurso)
41 | |+Shift-Cmd-T+ |Abrir un tipo
42 | |+Cmd-E+ |Cambiar entre algunas de las pestañas abiertas
43 | |======================
44 |
45 | Para ejecutar y depurar código
46 |
47 | [width="80%",options="header"]
48 | |======================
49 | |Atajo de teclado |Significado
50 | |+Alt-Cmd-X |Menú de ejecución (abre un desplegable con las /
51 | posibles opciones)
52 | |+Shift-Cmd-F11+ |Repetir la última ejecución
53 | |+Cmd-F11+ |Repetir la última depuración
54 | |======================
55 |
56 | Refactorizaciones
57 | -----------------
58 |
59 | Una de las razones más importantes por las que se utiliza un IDE es porque permite refactorizar el código rápidamente y simplificando increiblemente el proceso.
60 |
61 | Renombrar
62 | ~~~~~~~~~
63 |
64 | Los IDEs permiten el renombrado de variables, nombres de clases, paquetes, etc. En Eclipse se puede realizar mediante el uso del ratón y los menús contextuales, pero es más sencillo utilizar un atajo de teclas.
65 |
66 | Alt-Cmd-R
67 |
68 | Introducir variable
69 | ~~~~~~~~~~~~~~~~~~~
70 |
71 | Alt-Cmd-L
72 |
73 | Introducir método
74 | ~~~~~~~~~~~~~~~~~
75 |
76 | Si seleccionamos un bloque de código, lo podemos extraer como un método de forma automática, haciendo uso del siguiente atajo de teclado
77 |
78 | Alt-Cmd-M
79 |
80 | Reformatear
81 | -----------
82 |
83 | Shift-Cmd-F
84 |
85 | Generación de código
86 | --------------------
87 |
88 | Getters & Setters, comparadores
89 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90 |
91 | Al principio solemos escribir todo el código manualmente, pero es más productivo no tener que escribir código repetitivo. En un lenguaje como Java, los getters, setters o métodos como equals son muy comunes, para ello podemos hacer uso del siguiente atajo de teclado.
92 |
93 | Alt-Cmd-S
94 |
95 | Uso de templates
96 | ~~~~~~~~~~~~~~~~
97 |
98 | Hay una función muy interesante en Eclipse que es el uso de plantillas, que permiten generar bloques de código más complejos que posteriormente completaremos con el comportamiento deseado.
99 |
100 | Por ejemplo, se pueden generar métodos de test escribiendo tan solo "test" y el atajo de autocompletado (ctrl-espacio). Esta combinación generará el siguiente bloque.
101 |
102 | @Test
103 | public void testName() throws Exception {
104 | }
105 |
106 | Integración con control de versiones
107 | ------------------------------------
108 |
109 | Aunque Eclipse posee las herramientas necesarias para realizar control de versiones "Out of the box", solo lo hace con CSV que no es la opción más recomendable actualmente. Por suerte hay una gran cantidad de plugins que permite trabajar con SVN, Mercurial o Git entre otros.
110 |
111 | Haciendo uso de la integración con control de versiones, el propio IDE nos informa de los ficheros que tienen cambios, permite comparar con otras versiones del repositorio o realizar las operaciones más comunes sin necesidad de pasar por la consola del sistema.
112 |
113 | Otros sabores de Eclipse
114 | ------------------------
115 |
116 | Al tratarse de un software de código abierto, existen una gran variedad de diferentes "sabores" de Eclipse, enfocados a diferentes clases de proyectos o tecnologías. Por ejemplo está Spring Tools Suite más conocido como STS, si sueles utilizar el framework Spring, o MyEclipse que es una versión de pago en la que se incluyen gran cantidad de asistentes para simplificar el proceso de desarrollo, o Aptana Studio que intenta proveer mayor flexibilidad en el trabajo con ficheros HTML, JavaScript y CSS entre otros.
117 |
118 | No tienes que pelearte con nadie para demostrar cual es mejor o si Eclipse es mejor que otros IDEs tan solo debes conocer las ventajas que te aporta si algún día decides utilizarlo.
119 |
120 | Conclusiones
121 | ------------
122 |
123 | Si eres un gran conocedor del lenguaje o tecnología que estas utilizando, es posible que prefieras ahorrarte la cantidad de recursos que consume un IDE. Pero si como en mi caso, trabajas con sistemas de un tamaño relativamente grande, es muy posible que un IDE como Eclipse te ayude a mejorar enormemente tu productividad.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ARTICLE_SOURCE_FILES = funcionales documentacion_activa consejospruebas calidad integracion_continua tdd
2 | SOURCE_FILES = book.asc $(foreach article,$(ARTICLE_SOURCE_FILES),$(article).asc $(article)-biblio.asc)
3 | XSLT_OPTS = --xsltproc-opts="--stringparam chapter.autolabel 0 --stringparam chunk.section.depth 0 --stringparam toc.section.depth 0"
4 | BUILD_ID = $(shell git log -1 --format='Committed %ci %H')
5 | BUILD_LINK = $(shell git log -1 --format='Versión %ci %h')
6 | BUILD_ID_URL = $(shell git log -1 --format='https://www.assembla.com/code/proyecto-libro/git/changesets/%H')
7 |
8 | COMMIT_DATE_TIME = $(shell date --date="`git log -1 --format='%ci'`" --utc '+%Y-%m-%d')
9 | FILENAME = $(shell git log -1 --format='camino_$(COMMIT_DATE_TIME)_%h')
10 |
11 | libro: html epub chunked pdf
12 |
13 |
14 | html: $(FILENAME).html
15 |
16 | epub: $(FILENAME).epub
17 |
18 | chunked: $(FILENAME).chunked/index.html
19 |
20 | pdf: $(FILENAME).pdf
21 |
22 | .PHONY: html epub chunked pdf
23 |
24 | book.asc: book.asc.in Makefile
25 | sed -n '/#ARTICLELIST#/,$$ !p' book.asc.in >book.asc
26 | for i in $(ARTICLE_SOURCE_FILES); do \
27 | echo "include::$$i.asc[]\n" >>book.asc; \
28 | done
29 | sed -e '1,/#ARTICLELIST#/d' -e '/#BIBLIOLIST#/d' book.asc.in >>book.asc
30 | for i in $(ARTICLE_SOURCE_FILES); do \
31 | echo "include::$$i-biblio.asc[]\n" >>book.asc; \
32 | done
33 | sed -e '1,/#BIBLIOLIST#/d' book.asc.in >>book.asc
34 | sed 's/#BUILDID#/$(BUILD_ID)/' book.asc >book.asc-tmp && mv book.asc-tmp book.asc
35 |
36 | $(FILENAME).html: $(SOURCE_FILES)
37 | asciidoc -b html5 -a toc -a toclevels=1 -a themedir=`pwd`/theme-libro --theme=libro book.asc
38 | # Build id
39 | sed 's|^Last updated.*|$(BUILD_LINK)|' book.html >book.html-tmp && mv book.html-tmp book.html
40 | # Table of contents -> Índice general
41 | sed 's|Table of Contents|Índice general|' book.html >book.html-tmp && mv book.html-tmp book.html
42 | # Fix generation of dashes next to words (with no space in between)
43 | sed -e 's/ --\([^ ->]\)/ \—\1/g' -e 's/\([^<][^ -]\)--\([ ,\.:;)(]\)/\1\—\2/' book.html >book.html-tmp && \
44 | ./single-html-listing-title-hack.pl book.html-tmp >book.html && \
45 | rm book.html-tmp
46 | mv book.html $(FILENAME).html
47 |
48 | $(FILENAME).epub: book.xml libro.css
49 | a2x $(XSLT_OPTS) -f epub --stylesheet libro.css book.xml
50 | rm -rf epub-tmp
51 | mkdir epub-tmp && cd epub-tmp && unzip ../book.epub
52 | for i in epub-tmp/OEBPS/ch*.html; do \
53 | xmllint -format $$i >$$i.tmp; \
54 | ./fix-highlighting.py $$i.tmp >$$i; \
55 | ./chunked-html-listing-title-hack.pl $$i >$$i.tmp; \
56 | mv $$i.tmp $$i; \
57 | done
58 | ./fix-epub-toc.py epub-tmp/OEBPS/toc.ncx >epub-tmp/toc.ncx && mv epub-tmp/toc.ncx epub-tmp/OEBPS/toc.ncx
59 | ./add-epub-cover.sh epub-tmp/OEBPS/content.opf >epub-tmp/content.opf && mv epub-tmp/content.opf epub-tmp/OEBPS/content.opf
60 | cp cover.html epub-tmp/OEBPS
61 | cp cover.png epub-tmp/OEBPS
62 | cd epub-tmp && rm -f ../book.epub && zip -r ../$(FILENAME).epub *
63 |
64 | $(FILENAME).chunked/index.html: book.xml libro.css
65 | cp book.xml $(FILENAME).xml
66 | a2x $(XSLT_OPTS) -f chunked --stylesheet libro.css $(FILENAME).xml
67 | # Table of contents -> Índice general
68 | sed 's|Table of Contents|Índice general|' $(FILENAME).chunked/index.html >$(FILENAME).chunked/index.html-tmp && mv $(FILENAME).chunked/index.html-tmp $(FILENAME).chunked/index.html
69 | for i in $(FILENAME).chunked/ch*.html; do \
70 | xmllint -format $$i >$$i.tmp; \
71 | ./fix-highlighting.py $$i.tmp >$$i; \
72 | sed -e 's/ --\([^ ->]\)/ \—\1/g' -e 's/\([^<][^ -]\)--\([ ,\.:;)(]\)/\1\—\2/' $$i >$$i.tmp && \
73 | ./chunked-html-listing-title-hack.pl $$i.tmp >$$i; \
74 | rm $$i.tmp; \
75 | done
76 |
77 | book.xml: $(SOURCE_FILES)
78 | asciidoc -b docbook book.asc
79 | sed 's/]*>/&LANGUAGE=\1 /' book.xml >tmp.xml && mv tmp.xml book.xml
80 |
81 | cover.pdf: cover.png
82 | convert $< $@
83 |
84 | $(FILENAME).pdf: book.tex cover.pdf
85 | rm -rf tex-tmp
86 | mkdir tex-tmp
87 | cat $< >tex-tmp/book.tex
88 | convert cc-by-sa-big.png tex-tmp/cc-by-sa.pdf
89 | cd tex-tmp && \
90 | sed -e 's/[áéíóúñ]/_/gi' -e 's/cc-by-sa.png/cc-by-sa.pdf/g' book.tex >tmp.tex && \
91 | ../tex-listing-title-hack.pl tmp.tex >book.tex && \
92 | TEXINPUTS=/usr/share/dblatex/latex/style//::/etc/asciidoc/dblatex:/usr/share/dblatex/latex// pdflatex book.tex && \
93 | TEXINPUTS=/usr/share/dblatex/latex/style//::/etc/asciidoc/dblatex:/usr/share/dblatex/latex// pdflatex book.tex && \
94 | pdftk C=../cover.pdf B=book.pdf cat C B3-end output book-with-cover.pdf keep_final_id && \
95 | ../set_pdf_metadata.bash ../$(FILENAME).pdf 'El camino a un mejor programador' 'Varios Autores' $(BUILD_ID_URL)
96 |
97 | book.tex: $(SOURCE_FILES)
98 | a2x -a lang=es $(XSLT_OPTS) -f tex book.asc
99 | # Make the table of contents only have the article names
100 | sed 's/^\\setcounter{tocdepth}{[0-9]\+}/\\setcounter{tocdepth}{0}/' book.tex >book.tex-tmp && mv book.tex-tmp book.tex
101 | # Add Javascript and Scala language definition rules for the
102 | # "listings" package. Thanks to Lena Herrmann and Mark/Eivind
103 | # for the tip:
104 | # http://lenaherrmann.net/2010/05/20/javascript-syntax-highlighting-in-the-latex-listings-package
105 | # http://tex.stackexchange.com/questions/47175/scala-support-in-listings-package / http://tihlde.org/~eivindw/
106 | # Also, set secnumdepth to -1 so
107 | # those pesky "Chapter X" are not show at all
108 | sed 's/\\begin{document}/\\lstdefinelanguage{JavaScript}{keywords={typeof,new,true,false,catch,function,return,null,catch,switch,var,if,in,while,do,else,case,break},ndkeywords={class,export,boolean,throw,implements,import,this},sensitive=false,comment=[l]{\/\/},morecomment=[s]{\/*}{*\/},morestring=[b]'"'"',morestring=[b]"}\n% "define" Scala\n\\lstdefinelanguage{scala}{\nmorekeywords={abstract,case,catch,class,def,%\ndo,else,extends,false,final,finally,%\nfor,if,implicit,import,match,mixin,%\nnew,null,object,override,package,%\nprivate,protected,requires,return,sealed,%\nsuper,this,throw,trait,true,try,%\ntype,val,var,while,with,yield},\notherkeywords={=>,<-,<\\%,<:,>:,\\#,@},\nsensitive=true,\nmorecomment=[l]{\/\/},\nmorecomment=[n]{\/*}{*\/},\nmorestring=[b]",\nmorestring=[b]'"'"',\nmorestring=[b]"""\n}\n\\setcounter{secnumdepth}{-1}\n\\renewcommand\\contentsname{\\'"'"'Indice}\n&/' book.tex >book.tex-tmp && mv book.tex-tmp book.tex
109 | # Fix dashes, also probably only for Spanish
110 | sed -e 's/ -{}-{}\([^ -]\)/ \\textemdash{}\1/g' -e 's/\([^ -]\)-{}-{}\([ ,\.:;)(]\)/\1\\textemdash{}\2/' book.tex >book.tex-tmp && mv book.tex-tmp book.tex
111 |
112 | clean:
113 | rm -rf tex-tmp epub-tmp
114 | rm -f book.tex cover.pdf
115 | rm -rf camino_*.chunked camino_*.pdf camino_*.html camino_*.epub
116 | rm -f camino_*.xml
117 |
--------------------------------------------------------------------------------
/libro.css:
--------------------------------------------------------------------------------
1 | span.strong {
2 | font-weight: bold;
3 | }
4 |
5 | body blockquote {
6 | color: #888888;
7 | line-height: 1.5;
8 | margin-top: .75em;
9 | margin-bottom: .75em;
10 | margin-left: 1em;
11 | margin-right: 10%;
12 | border-left: 5px solid #F0F0F0;
13 | padding-left: 1em;
14 | }
15 | blockquote p {
16 | line-height: 1.1;
17 | }
18 |
19 | html body {
20 | margin: 1em 5% 1em 5%;
21 | line-height: 1.2;
22 | font-family: Georgia;
23 | }
24 |
25 | body div {
26 | margin: 0;
27 | }
28 |
29 | h1, h2, h3, h4, h5, h6
30 | {
31 | color: #527bbd;
32 | font-family: tahoma, verdana, sans-serif;
33 | }
34 |
35 | p[title] {
36 | color: #888;
37 | font-family: "Arial", "Helvetica", sans-serif;
38 | margin-bottom: 0.5em;
39 | margin-top: 0.5ex;
40 | font-size: 90%;
41 | }
42 |
43 | p[title] strong {
44 | font-weight: inherit;
45 | }
46 |
47 | div.toc p:first-child,
48 | div.list-of-figures p:first-child,
49 | div.list-of-tables p:first-child,
50 | div.list-of-examples p:first-child,
51 | div.example p.title,
52 | div.sidebar p.title
53 | {
54 | font-weight: bold;
55 | color: #527bbd;
56 | font-family: tahoma, verdana, sans-serif;
57 | margin-bottom: 0.2em;
58 | }
59 |
60 | body h1 {
61 | margin: .0em 0 0 -4%;
62 | line-height: 1.3;
63 | border-bottom: 2px solid silver;
64 | }
65 |
66 | body h2 {
67 | margin: 0.5em 0 0 -4%;
68 | line-height: 1.3;
69 | border-bottom: 2px solid silver;
70 | }
71 |
72 | body h3 {
73 | margin: .8em 0 0 -3%;
74 | line-height: 1.3;
75 | }
76 |
77 | body h4 {
78 | margin: .8em 0 0 -3%;
79 | line-height: 1.3;
80 | }
81 |
82 | body h5 {
83 | margin: .8em 0 0 -2%;
84 | line-height: 1.3;
85 | }
86 |
87 | body h6 {
88 | margin: .8em 0 0 -1%;
89 | line-height: 1.3;
90 | }
91 |
92 | body hr {
93 | border: none; /* Broken on IE6 */
94 | }
95 | div.footnotes hr {
96 | border: 1px solid silver;
97 | }
98 |
99 | div.navheader th, div.navheader td, div.navfooter td {
100 | font-family: sans-serif;
101 | font-size: 0.9em;
102 | font-weight: bold;
103 | color: #527bbd;
104 | }
105 | div.navheader img, div.navfooter img {
106 | border-style: none;
107 | }
108 | div.navheader a, div.navfooter a {
109 | font-weight: normal;
110 | }
111 | div.navfooter hr {
112 | border: 1px solid silver;
113 | }
114 |
115 | body td {
116 | line-height: 1.2
117 | }
118 |
119 | body th {
120 | line-height: 1.2;
121 | }
122 |
123 | ol {
124 | line-height: 1.2;
125 | }
126 |
127 | ul, body dir, body menu {
128 | line-height: 1.2;
129 | }
130 |
131 | html {
132 | margin: 0;
133 | padding: 0;
134 | }
135 |
136 | body h1, body h2, body h3, body h4, body h5, body h6 {
137 | margin-left: 0
138 | }
139 |
140 | body pre {
141 | margin: 0.5em 10% 0.5em 1em;
142 | color: navy;
143 | font-family: Georgia;
144 | font-size: inherit;
145 | }
146 |
147 | tt.literal, code.literal {
148 | color: navy;
149 | }
150 |
151 | .programlisting, .screen {
152 | margin: 0.5em 10% 0 0;
153 | padding: 0.5em;
154 | background: #F8F8F8;
155 | border: 1px solid #DDDDDD;
156 | border-left: 5px solid #F0F0F0;
157 | }
158 |
159 | div.sidebar {
160 | background: #ffffee;
161 | margin: 1.0em 10% 0.5em 0;
162 | padding: 0.5em 1em;
163 | border: 1px solid silver;
164 | }
165 | div.sidebar * { padding: 0; }
166 | div.sidebar div { margin: 0; }
167 | div.sidebar p.title {
168 | margin-top: 0.5em;
169 | margin-bottom: 0.2em;
170 | }
171 |
172 | div.bibliomixed {
173 | margin: 0.5em 5% 0.5em 1em;
174 | }
175 |
176 | div.glossary dt {
177 | font-weight: bold;
178 | }
179 | div.glossary dd p {
180 | margin-top: 0.2em;
181 | }
182 |
183 | dl {
184 | margin: .8em 0;
185 | line-height: 1.2;
186 | }
187 |
188 | dt {
189 | margin-top: 0.5em;
190 | }
191 |
192 | dt span.term {
193 | font-style: normal;
194 | color: navy;
195 | }
196 |
197 | div.variablelist dd p {
198 | margin-top: 0;
199 | }
200 |
201 | div.itemizedlist li, div.orderedlist li {
202 | margin-left: -0.8em;
203 | margin-top: 0.5em;
204 | }
205 |
206 | ul, ol {
207 | list-style-position: outside;
208 | }
209 |
210 | div.sidebar ul, div.sidebar ol {
211 | margin-left: 2.8em;
212 | }
213 |
214 | div.itemizedlist p.title,
215 | div.orderedlist p.title,
216 | div.variablelist p.title
217 | {
218 | margin-bottom: -0.8em;
219 | }
220 |
221 | div.revhistory table {
222 | border-collapse: collapse;
223 | border: none;
224 | }
225 | div.revhistory th {
226 | border: none;
227 | color: #527bbd;
228 | font-family: tahoma, verdana, sans-serif;
229 | }
230 | div.revhistory td {
231 | border: 1px solid silver;
232 | }
233 |
234 | /* Keep TOC and index lines close together. */
235 | div.toc dl, div.toc dt,
236 | div.list-of-figures dl, div.list-of-figures dt,
237 | div.list-of-tables dl, div.list-of-tables dt,
238 | div.indexdiv dl, div.indexdiv dt
239 | {
240 | line-height: normal;
241 | margin-top: 0;
242 | margin-bottom: 0;
243 | }
244 |
245 | /*
246 | Table styling does not work because of overriding attributes in
247 | generated HTML.
248 | */
249 | div.table table,
250 | div.informaltable table
251 | {
252 | margin-left: 0;
253 | margin-right: 5%;
254 | margin-bottom: 0.8em;
255 | }
256 | div.informaltable table
257 | {
258 | margin-top: 0.4em
259 | }
260 | div.table thead,
261 | div.table tfoot,
262 | div.table tbody,
263 | div.informaltable thead,
264 | div.informaltable tfoot,
265 | div.informaltable tbody
266 | {
267 | /* No effect in IE6. */
268 | border-top: 3px solid #527bbd;
269 | border-bottom: 3px solid #527bbd;
270 | }
271 | div.table thead, div.table tfoot,
272 | div.informaltable thead, div.informaltable tfoot
273 | {
274 | font-weight: bold;
275 | }
276 |
277 | div.mediaobject img {
278 | margin-bottom: 0.8em;
279 | }
280 | div.figure p.title,
281 | div.table p.title
282 | {
283 | margin-top: 1em;
284 | margin-bottom: 0.4em;
285 | }
286 |
287 | div.calloutlist p
288 | {
289 | margin-top: 0em;
290 | margin-bottom: 0.4em;
291 | }
292 |
293 | a img {
294 | border-style: none;
295 | }
296 |
297 | @media print {
298 | div.navheader, div.navfooter { display: none; }
299 | }
300 |
301 | span.aqua { color: aqua; }
302 | span.black { color: black; }
303 | span.blue { color: blue; }
304 | span.fuchsia { color: fuchsia; }
305 | span.gray { color: gray; }
306 | span.green { color: green; }
307 | span.lime { color: lime; }
308 | span.maroon { color: maroon; }
309 | span.navy { color: navy; }
310 | span.olive { color: olive; }
311 | span.purple { color: purple; }
312 | span.red { color: red; }
313 | span.silver { color: silver; }
314 | span.teal { color: teal; }
315 | span.white { color: white; }
316 | span.yellow { color: yellow; }
317 |
318 | span.aqua-background { background: aqua; }
319 | span.black-background { background: black; }
320 | span.blue-background { background: blue; }
321 | span.fuchsia-background { background: fuchsia; }
322 | span.gray-background { background: gray; }
323 | span.green-background { background: green; }
324 | span.lime-background { background: lime; }
325 | span.maroon-background { background: maroon; }
326 | span.navy-background { background: navy; }
327 | span.olive-background { background: olive; }
328 | span.purple-background { background: purple; }
329 | span.red-background { background: red; }
330 | span.silver-background { background: silver; }
331 | span.teal-background { background: teal; }
332 | span.white-background { background: white; }
333 | span.yellow-background { background: yellow; }
334 |
335 | span.big { font-size: 2em; }
336 | span.small { font-size: 0.6em; }
337 |
338 | span.underline { text-decoration: underline; }
339 | span.overline { text-decoration: overline; }
340 | span.line-through { text-decoration: line-through; }
341 |
--------------------------------------------------------------------------------
/book.asc.in:
--------------------------------------------------------------------------------
1 | El camino a un mejor programador
2 | ================================
3 | Esteban Manchado Velázquez, Joaquín Caraballo Moreno, Yeray Darias Camacho
4 | :doctype: book
5 |
6 | :leveloffset: 1
7 |
8 | Licencia
9 | ========
10 |
11 | Esta obra se distribuye bajo la http://creativecommons.org/licenses/by-sa/3.0/deed.es_ES[licencia Creative Commons Reconocimiento-CompartirIgual 3.0]. Esto significa que usted puede:
12 |
13 | * copiar, distribuir y comunicar públicamente la obra
14 | * remezclar: transformar la obra
15 | * hacer un uso comercial de esta obra
16 |
17 | Bajo las condiciones siguientes:
18 |
19 | * Reconocimiento: debe reconocer los créditos de la obra de la manera especificada por el autor o el licenciador (pero no de una manera que sugiera que tiene su apoyo o apoyan el uso que hace de su obra).
20 | * Compartir bajo la misma licencia: si altera o transforma esta obra, o genera una obra derivada, sólo puede distribuir la obra generada bajo una licencia idéntica a ésta.
21 |
22 | Entendiendo que:
23 |
24 | * Renuncia: alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor
25 | * Dominio público: cuando la obra o alguno de sus elementos se halle en el dominio público según la ley vigente aplicable, esta situación no quedará afectada por la licencia.
26 | * Otros derechos: los derechos siguientes no quedan afectados por la licencia de ninguna manera:
27 | ** Los derechos derivados de usos legítimos u otras limitaciones reconocidas por ley no se ven afectados por lo anterior.
28 | ** Los derechos morales del autor;
29 | ** Derechos que pueden ostentar otras personas sobre la propia obra o su uso, como por ejemplo derechos de imagen o de privacidad.
30 | * Aviso: al reutilizar o distribuir la obra, tiene que dejar bien claro los términos de la licencia de esta obra.
31 |
32 | image:cc-by-sa.png[Creative Common Reconocimiento-CompartirIgual 3.0]
33 |
34 | Agradecimientos
35 | ===============
36 |
37 | A todos aquellos que ayudaron de una manera u otra en la creación de
38 | este libro, con consejos, apoyo o ideas. Las siguientes personas se
39 | merecen una mención especial:
40 |
41 | * Sara Pettersson, por la portada
42 | * Mario Hernández, por el prólogo
43 | * Carlos Ble, por ayudar con la revisión de varios artículos y por sus
44 | ideas y consejos
45 | * Juanje Ojeda, por sus ideas y empuje iniciales
46 |
47 | Prólogo
48 | =======
49 |
50 | Se cuenta que una vez le preguntaron a Miguel Ángel cómo había procedido para esculpir el David. Su respuesta fue que lo que había hecho era, simplemente, eliminar del bloque de piedra original todo lo que sobraba. Independientemente de la verosimilitud de esta anécdota, podemos encontrar en ella la enseñanza de una actitud a tomar para abordar problemas prácticos de desarrollo en ingeniería.
51 |
52 | Si la pregunta se le hiciese a un escultor que modelase en barro la respuesta equivalente sería, posiblemente, que lo que había hecho era rellenar el espacio disponiendo el barro estrictamente necesario en las zonas adecuadas. Estas dos respuestas se refieren a dos aproximaciones para abordar procesos constructivos: la sustractiva, natural en la escultura en piedra, en la que se eliminan elementos de una solución primigenia hasta que se cumple con las restricciones de la solución definitiva, y la aditiva en la que se aborda el problema partiendo de una solución inicial vacía, añadiendo elementos según ciertos criterios con el fin de conseguir que la solución final cumpla los objetivos de diseño. Estas dos son aproximaciones extrapolables a otras disciplinas y en general, en muchos otros casos, la solución se alcanza con aproximaciones mixtas, en las que se opera con acciones aditivas y sustractivas combinadas.
53 |
54 | El desarrollo de software es una disciplina práctica relativamente nueva que tiene poco más de medio siglo de existencia, pero que conceptualmente es hija de las disciplinas clásicas constructivas englobadas bajo el paraguas de las ingenierías y la arquitectura. De ellas hereda una de las habilidades que necesita un buen informático; la de resolver problemas. Por ella me refiero a la capacidad, en palabras de Allen Downey, el autor de «Think Python. How to Think Like a Computer Scientist», para formular problemas, pensar en soluciones de manera creativa, y expresarlas de forma clara y precisa. A esto se debe probablemente mucha de la genialidad de Miguel Ángel.
55 |
56 | Volviendo a las disciplinas clásicas de la ingeniería y la arquitectura, el objetivo de estas no es el de las ciencias clásicas de carácter analítico, es decir, no intenta entender, explicar y describir algo ya existente como lo hacen la física, la química o la biología, sino más bien sintético, es decir, orientado a desarrollar y construir cosas nuevas que satisfagan un objetivo expresado normalmente por la función definida a priori que debe cumplir el resultado. En este ámbito, el conocimiento acerca de cómo realizar bien las tareas de desarrollo de nuevas soluciones es un trabajo de destilación que se produce en ciclos de vida resumibles en «observación-análisis-propuestas de solución-realización práctica-evaluación de resultados-refinamiento, para alcanzar el nivel de aceptación-corrección para iniciar de nuevo el ciclo o descarte de la propuesta» según corresponda. Estos procesos se realizan iterativamente en el tiempo, constituyendo la fuente del conocimiento práctico del saber hacer de la disciplina y que se terminan expresando como recetas metodológicas y conjuntos de buenas prácticas para ejecutar los proyectos y tareas futuros. Su fin es llevar esos proyectos a buen término en forma de una solución que sea eficiente y efectiva y cumpla las restricciones impuestas al mínimo coste de realización posible medido en horas-hombre. Este proceso es análogo, de nuevo, a los desarrollos de la ingeniería y la arquitectura primigenia, donde los constructores y albañiles desarrollaban su conocimiento y metodologías de forma práctica enfrentándose a retos y problemas nuevos. Ello permitió, por ejemplo, construir las grandes obras de ingeniería civil durante la época de esplendor de Roma, como es el caso de los acueductos, palacios o coliseos, o la gran arquitectura revolucionaria de las catedrales de estilo gótico. Esas construcciones fueron productos pero también retos, que permitieron desarrollar conocimiento y metodologías que se explotaron y depuraron durante siglos.
57 |
58 | La realización de proyectos en disciplinas sintéticas es pues una combinación virtuosa de la concepción y uso de las herramientas adecuadas para el desarrollo, con el conocimiento, las habilidades con esas herramientas y procesos y la experiencia disponible en la mente de quienes los ejecutan, que se manifiesta en la capacidad de resolución de problemas y se expresa como recetas metodológicas y conjuntos de buenas prácticas para llevar a buen fin los proyectos por parte de equipos de personas cualificadas. Una buena ingeniería o una buena arquitectura podrían entenderse en el sentido de Miguel Ángel como las disciplinas que tienen por objetivo el uso adecuado del conjunto de herramientas, conocimiento y habilidades combinados con una metodología que permite construir lo nuevo que se propone cumpliendo los objetivos de diseño, buscando quedarnos con lo estrictamente necesario en sentido constructivo y eliminando lo superfluo.
59 |
60 | ¿Cuál es la diferencia entre el desarrollo de proyectos de programación y el desarrollo de proyectos de ingeniería o arquitectura más o menos convencionales? Pues, conceptualmente el único conjunto de diferencias surge, básicamente, de la naturaleza del objeto de trabajo. En el caso de los últimos, el objeto lo constituyen tangibles físicos, mientras que en el caso del primero, el objeto es fundamentalmente un intangible: todo lo relacionado con la información y sus transformaciones. Además, por la naturaleza de su resultado y al contrario que en otras disciplinas constructivas, el objeto de la ejecución de proyectos software no está sometido a ley física alguna, excepto la que regula la entropía. De hecho, una gran parte de la actividad metodológica en el desarrollo de proyectos está dirigida a luchar contra la degradación y el desorden. Esta situación es la que establece la particularidad de la ejecución de proyectos software como cuerpo disciplinar, y es la que delimita el territorio sobre el que se tienen que desarrollar herramientas, conocimiento y metodologías.
61 |
62 | Este libro pretende ser una aportación a la recopilación y la transmisión del conocimiento y la experiencia de un grupo de buenos y experimentados programadores profesionales en el desarrollo de proyectos de programación. Constituye pues un documento valioso para aquellas personas interesados en el mundo profesional del software.
63 |
64 | Un valor interesante del libro reside en que se dedica a temas que no suelen formar parte del flujo de conocimientos que se manejan usualmente en los procesos de formación básica de los desarrolladores e ingenieros de software, como son los que aparecen en el índice de este documento: el desarrollo de software bajo el paradigma de programación funcional, la documentación activa, la prueba de programas, la calidad del software o los procesos de integración continua. Más bien podemos entenderlo como un texto que intenta servir como transmisor de conocimiento experto desde quienes tienen años de experiencia profesional en el campo y se dirige hacia un amplio espectro de informáticos, tanto experimentados en otros saberes, como noveles en estos temas. En este sentido constituye una obra de valor en los ámbitos que le compete.
65 |
66 | Espero y confío en que esta contribución sirva para los fines que se propusieron sus autores y resulte de utilidad para sus lectores.
67 |
68 | Mario Hernández +
69 | Catedrático de Universidad en Ciencias de la Computación e Inteligencia Artificial +
70 | Las Palmas de Gran Canaria, octubre de 2012
71 |
72 | #ARTICLELIST#
73 |
74 | [bibliography]
75 | Bibliografía
76 | ============
77 | #BIBLIOLIST#
78 |
--------------------------------------------------------------------------------
/theme-libro/libro.css:
--------------------------------------------------------------------------------
1 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
2 |
3 | /* Default font. */
4 | body {
5 | font-family: Georgia,serif;
6 | }
7 |
8 | /* Title font. */
9 | h1, h2, h3, h4, h5, h6,
10 | div.title, caption.title,
11 | thead, p.table.header,
12 | #toctitle,
13 | #author, #revnumber, #revdate, #revremark,
14 | #footer {
15 | font-family: Arial,Helvetica,sans-serif;
16 | }
17 |
18 | body {
19 | margin: 1em 5% 1em 5%;
20 | }
21 |
22 | a {
23 | color: blue;
24 | text-decoration: underline;
25 | }
26 | a:visited {
27 | color: fuchsia;
28 | }
29 |
30 | em {
31 | font-style: italic;
32 | }
33 |
34 | strong {
35 | font-weight: bold;
36 | color: #083194;
37 | }
38 |
39 | h1, h2, h3, h4, h5, h6 {
40 | color: #527bbd;
41 | margin-top: 1.2em;
42 | margin-bottom: 0.5em;
43 | line-height: 1.3;
44 | }
45 |
46 | h1, h2, h3 {
47 | border-bottom: 2px solid silver;
48 | }
49 | h2 {
50 | padding-top: 0.5em;
51 | }
52 | h3 {
53 | float: left;
54 | }
55 | h3 + * {
56 | clear: left;
57 | }
58 | h5 {
59 | font-size: 1.0em;
60 | }
61 |
62 | div.sectionbody {
63 | margin-left: 0;
64 | }
65 |
66 | hr {
67 | border: 1px solid silver;
68 | }
69 |
70 | p {
71 | margin-top: 0.5em;
72 | margin-bottom: 0.5em;
73 | }
74 |
75 | ul, ol, li > p {
76 | margin-top: 0;
77 | }
78 | ul > li { color: #aaa; }
79 | ul > li > * { color: black; }
80 |
81 | pre {
82 | padding: 0;
83 | margin: 0;
84 | }
85 |
86 | #author {
87 | color: #527bbd;
88 | font-weight: bold;
89 | font-size: 1.1em;
90 | }
91 | #email {
92 | }
93 | #revnumber, #revdate, #revremark {
94 | }
95 |
96 | #footer {
97 | font-size: small;
98 | border-top: 2px solid silver;
99 | padding-top: 0.5em;
100 | margin-top: 4.0em;
101 | }
102 | #footer-text {
103 | float: left;
104 | padding-bottom: 0.5em;
105 | }
106 | #footer-badges {
107 | float: right;
108 | padding-bottom: 0.5em;
109 | }
110 |
111 | #preamble {
112 | margin-top: 1.5em;
113 | margin-bottom: 1.5em;
114 | }
115 | div.imageblock, div.exampleblock, div.verseblock,
116 | div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
117 | div.admonitionblock {
118 | margin-top: 1.0em;
119 | }
120 | div.admonitionblock {
121 | margin-top: 2.0em;
122 | margin-bottom: 2.0em;
123 | margin-right: 10%;
124 | color: #606060;
125 | }
126 |
127 | div.content { /* Block element content. */
128 | padding: 0;
129 | }
130 |
131 | /* Block element titles. */
132 | div.title, caption.title {
133 | color: #888888;
134 | font-size: 90%;
135 | font-style: italic;
136 | text-align: left;
137 | margin-top: 0.5ex;
138 | margin-bottom: 1em;
139 | }
140 | div.title + * {
141 | margin-top: 0;
142 | }
143 |
144 | td div.title:first-child {
145 | margin-top: 0.0em;
146 | }
147 | div.content div.title:first-child {
148 | margin-top: 0.0em;
149 | }
150 | div.content + div.title {
151 | margin-top: 0.0em;
152 | }
153 |
154 | div.sidebarblock > div.content {
155 | background: #ffffee;
156 | border: 1px solid #dddddd;
157 | border-left: 4px solid #f0f0f0;
158 | padding: 0.5em;
159 | }
160 |
161 | div.listingblock > div.content {
162 | border: 1px solid #dddddd;
163 | border-left: 5px solid #f0f0f0;
164 | background: #f8f8f8;
165 | padding: 0.5em;
166 | }
167 |
168 | div.quoteblock, div.verseblock {
169 | padding-left: 1.0em;
170 | margin-left: 1.0em;
171 | margin-right: 10%;
172 | border-left: 5px solid #f0f0f0;
173 | color: #888;
174 | }
175 |
176 | div.quoteblock > div.attribution {
177 | padding-top: 0.5em;
178 | text-align: right;
179 | }
180 |
181 | div.verseblock > pre.content {
182 | font-family: inherit;
183 | font-size: inherit;
184 | }
185 | div.verseblock > div.attribution {
186 | padding-top: 0.75em;
187 | text-align: left;
188 | }
189 | /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
190 | div.verseblock + div.attribution {
191 | text-align: left;
192 | }
193 |
194 | div.admonitionblock .icon {
195 | vertical-align: top;
196 | font-size: 1.1em;
197 | font-weight: bold;
198 | text-decoration: underline;
199 | color: #527bbd;
200 | padding-right: 0.5em;
201 | }
202 | div.admonitionblock td.content {
203 | padding-left: 0.5em;
204 | border-left: 3px solid #dddddd;
205 | }
206 |
207 | div.exampleblock > div.content {
208 | border-left: 3px solid #dddddd;
209 | padding-left: 0.5em;
210 | }
211 |
212 | div.imageblock div.content { padding-left: 0; }
213 | span.image img { border-style: none; }
214 | a.image:visited { color: white; }
215 |
216 | dl {
217 | margin-top: 0.8em;
218 | margin-bottom: 0.8em;
219 | }
220 | dt {
221 | margin-top: 0.5em;
222 | margin-bottom: 0;
223 | font-style: normal;
224 | color: navy;
225 | }
226 | dd > *:first-child {
227 | margin-top: 0.1em;
228 | }
229 |
230 | ul, ol {
231 | list-style-position: outside;
232 | }
233 | ol.arabic {
234 | list-style-type: decimal;
235 | }
236 | ol.loweralpha {
237 | list-style-type: lower-alpha;
238 | }
239 | ol.upperalpha {
240 | list-style-type: upper-alpha;
241 | }
242 | ol.lowerroman {
243 | list-style-type: lower-roman;
244 | }
245 | ol.upperroman {
246 | list-style-type: upper-roman;
247 | }
248 |
249 | div.compact ul, div.compact ol,
250 | div.compact p, div.compact p,
251 | div.compact div, div.compact div {
252 | margin-top: 0.1em;
253 | margin-bottom: 0.1em;
254 | }
255 |
256 | tfoot {
257 | font-weight: bold;
258 | }
259 | td > div.verse {
260 | white-space: pre;
261 | }
262 |
263 | div.hdlist {
264 | margin-top: 0.8em;
265 | margin-bottom: 0.8em;
266 | }
267 | div.hdlist tr {
268 | padding-bottom: 15px;
269 | }
270 | dt.hdlist1.strong, td.hdlist1.strong {
271 | font-weight: bold;
272 | }
273 | td.hdlist1 {
274 | vertical-align: top;
275 | font-style: normal;
276 | padding-right: 0.8em;
277 | color: navy;
278 | }
279 | td.hdlist2 {
280 | vertical-align: top;
281 | }
282 | div.hdlist.compact tr {
283 | margin: 0;
284 | padding-bottom: 0;
285 | }
286 |
287 | .comment {
288 | background: yellow;
289 | }
290 |
291 | .footnote, .footnoteref {
292 | font-size: 0.8em;
293 | }
294 |
295 | span.footnote, span.footnoteref {
296 | vertical-align: super;
297 | }
298 |
299 | #footnotes {
300 | margin: 20px 0 20px 0;
301 | padding: 7px 0 0 0;
302 | }
303 |
304 | #footnotes div.footnote {
305 | margin: 0 0 5px 0;
306 | }
307 |
308 | #footnotes hr {
309 | border: none;
310 | border-top: 1px solid silver;
311 | height: 1px;
312 | text-align: left;
313 | margin-left: 0;
314 | width: 20%;
315 | min-width: 100px;
316 | }
317 |
318 | div.colist td {
319 | padding-right: 0.5em;
320 | padding-bottom: 0.3em;
321 | vertical-align: top;
322 | }
323 | div.colist td img {
324 | margin-top: 0.3em;
325 | }
326 |
327 | @media print {
328 | #footer-badges { display: none; }
329 | }
330 |
331 | #toc {
332 | margin-bottom: 2.5em;
333 | }
334 |
335 | #toctitle {
336 | color: #527bbd;
337 | font-size: 1.1em;
338 | font-weight: bold;
339 | margin-top: 1.0em;
340 | margin-bottom: 0.1em;
341 | }
342 |
343 | div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
344 | margin-top: 0;
345 | margin-bottom: 0;
346 | }
347 | div.toclevel2 {
348 | margin-left: 2em;
349 | font-size: 0.9em;
350 | }
351 | div.toclevel3 {
352 | margin-left: 4em;
353 | font-size: 0.9em;
354 | }
355 | div.toclevel4 {
356 | margin-left: 6em;
357 | font-size: 0.9em;
358 | }
359 |
360 | span.aqua { color: aqua; }
361 | span.black { color: black; }
362 | span.blue { color: blue; }
363 | span.fuchsia { color: fuchsia; }
364 | span.gray { color: gray; }
365 | span.green { color: green; }
366 | span.lime { color: lime; }
367 | span.maroon { color: maroon; }
368 | span.navy { color: navy; }
369 | span.olive { color: olive; }
370 | span.purple { color: purple; }
371 | span.red { color: red; }
372 | span.silver { color: silver; }
373 | span.teal { color: teal; }
374 | span.white { color: white; }
375 | span.yellow { color: yellow; }
376 |
377 | span.aqua-background { background: aqua; }
378 | span.black-background { background: black; }
379 | span.blue-background { background: blue; }
380 | span.fuchsia-background { background: fuchsia; }
381 | span.gray-background { background: gray; }
382 | span.green-background { background: green; }
383 | span.lime-background { background: lime; }
384 | span.maroon-background { background: maroon; }
385 | span.navy-background { background: navy; }
386 | span.olive-background { background: olive; }
387 | span.purple-background { background: purple; }
388 | span.red-background { background: red; }
389 | span.silver-background { background: silver; }
390 | span.teal-background { background: teal; }
391 | span.white-background { background: white; }
392 | span.yellow-background { background: yellow; }
393 |
394 | span.big { font-size: 2em; }
395 | span.small { font-size: 0.6em; }
396 |
397 | span.underline { text-decoration: underline; }
398 | span.overline { text-decoration: overline; }
399 | span.line-through { text-decoration: line-through; }
400 |
401 | div.unbreakable { page-break-inside: avoid; }
402 |
403 |
404 | /*
405 | * xhtml11 specific
406 | *
407 | * */
408 |
409 | tt {
410 | line-height: 1.2;
411 | font-size: inherit;
412 | color: navy;
413 | }
414 |
415 | div.tableblock {
416 | margin-top: 1.0em;
417 | margin-bottom: 1.5em;
418 | }
419 | div.tableblock > table {
420 | border: 3px solid #527bbd;
421 | }
422 | thead, p.table.header {
423 | font-weight: bold;
424 | color: #527bbd;
425 | }
426 | p.table {
427 | margin-top: 0;
428 | }
429 | /* Because the table frame attribute is overriden by CSS in most browsers. */
430 | div.tableblock > table[frame="void"] {
431 | border-style: none;
432 | }
433 | div.tableblock > table[frame="hsides"] {
434 | border-left-style: none;
435 | border-right-style: none;
436 | }
437 | div.tableblock > table[frame="vsides"] {
438 | border-top-style: none;
439 | border-bottom-style: none;
440 | }
441 |
442 |
443 | /*
444 | * html5 specific
445 | *
446 | * */
447 |
448 | .monospaced {
449 | font-family: "Courier New", Courier, monospace;
450 | font-size: inherit;
451 | color: navy;
452 | }
453 |
454 | table.tableblock {
455 | margin-top: 1.0em;
456 | margin-bottom: 1.5em;
457 | }
458 | thead, p.tableblock.header {
459 | font-weight: bold;
460 | color: #527bbd;
461 | }
462 | p.tableblock {
463 | margin-top: 0;
464 | }
465 | table.tableblock {
466 | border-width: 3px;
467 | border-spacing: 0px;
468 | border-style: solid;
469 | border-color: #527bbd;
470 | border-collapse: collapse;
471 | }
472 | th.tableblock, td.tableblock {
473 | border-width: 1px;
474 | padding: 4px;
475 | border-style: solid;
476 | border-color: #527bbd;
477 | }
478 |
479 | table.tableblock.frame-topbot {
480 | border-left-style: hidden;
481 | border-right-style: hidden;
482 | }
483 | table.tableblock.frame-sides {
484 | border-top-style: hidden;
485 | border-bottom-style: hidden;
486 | }
487 | table.tableblock.frame-none {
488 | border-style: hidden;
489 | }
490 |
491 | th.tableblock.halign-left, td.tableblock.halign-left {
492 | text-align: left;
493 | }
494 | th.tableblock.halign-center, td.tableblock.halign-center {
495 | text-align: center;
496 | }
497 | th.tableblock.halign-right, td.tableblock.halign-right {
498 | text-align: right;
499 | }
500 |
501 | th.tableblock.valign-top, td.tableblock.valign-top {
502 | vertical-align: top;
503 | }
504 | th.tableblock.valign-middle, td.tableblock.valign-middle {
505 | vertical-align: middle;
506 | }
507 | th.tableblock.valign-bottom, td.tableblock.valign-bottom {
508 | vertical-align: bottom;
509 | }
510 |
511 |
512 | /*
513 | * manpage specific
514 | *
515 | * */
516 |
517 | body.manpage h1 {
518 | padding-top: 0.5em;
519 | padding-bottom: 0.5em;
520 | border-top: 2px solid silver;
521 | border-bottom: 2px solid silver;
522 | }
523 | body.manpage h2 {
524 | border-style: none;
525 | }
526 | body.manpage div.sectionbody {
527 | margin-left: 3em;
528 | }
529 |
530 | @media print {
531 | body.manpage div#toc { display: none; }
532 | }
533 |
--------------------------------------------------------------------------------
/documentacion_activa.asc.in:
--------------------------------------------------------------------------------
1 | Documentación activa
2 | ====================
3 | Joaquín Caraballo
4 |
5 | Si en un extremo están los que definen el problema a resolver y en el otro los que lo resuelven, ¿cómo podemos asegurarnos de que todos estamos intentando resolver el mismo problema? Y, si aceptamos que la definición va a necesitar ser enriquecida, corregida, matizada, e incluso que el problema a resolver va a cambiar radicalmente durante la vida del proyecto, ¿cómo mantenemos una misma definición para todos los interesados, y más importante, cómo aseguramos que nuestro programa resuelve el problema?
6 |
7 |
8 | Las pruebas funcionales
9 | -----------------------
10 |
11 | Para tener una cierta confianza en que la aplicación resuelve un cierto problema se llevan a cabo pruebas funcionales; estas resuelven un ejemplo concreto y representativo simulando las acciones del usuario y verificando que la reacción de la aplicación es la esperada.
12 |
13 | Las pruebas funcionales han de mantenerse al día y evolucionar con los cambios de la aplicación y sus requisitos. Otro de los retos es que sean correctas, es decir, que el comportamiento que estén verificando sea realmente el que se requiere de la aplicación.
14 |
15 | Es crucial que las pruebas funcionales estén automatizadas; lo típico es escribirlas en el mismo lenguaje de programación de la aplicación. Esto nos permite ejecutar un gran número de ellas de forma sistemática y tras cada cambio, con lo que también nos protegerán de que algo que funcionaba deje de hacerlo, es decir, de posibles _regresiones_.
16 |
17 |
18 | neverread
19 | ~~~~~~~~~
20 |
21 | Supongamos que tenemos un cliente con falta de tiempo para leer artículos de Internet.
22 |
23 | ____________________________________________________________________
24 | -Tantos artículos interesantes, no tengo tiempo para leerlos, pero tampoco puedo ignorarlos. Me gustaría tener una aplicación en la que copiar la dirección del artículo que me interese.
25 |
26 | -¡Ah!, ¿quieres una aplicación que te permita almacenar enlaces a artículos? La aplicación mantendría una lista con los artículos almacenados, a la que luego podrías acceder cuando tengas tiempo y leer los artículos...
27 |
28 | -No, no, si yo lo que quiero es que los enlaces a artículos desaparezcan, si tuviera tiempo para leerlos después usaría instapaper, hombre. Estaría bien que tuviese una lista de artículos por leer siempre vacía, me daría una sensación genial de tenerlo todo bajo control.
29 |
30 | -Er... vale.
31 | ____________________________________________________________________
32 |
33 | De la conversación hemos conseguido extraer un criterio de aceptación:
34 | ____
35 | Por mucho que añadamos artículos, estos no se añadirán a una lista de artículos por leer.
36 | ____
37 |
38 | La prueba funcional correspondiente, verificará que la aplicación cumple el criterio para un ejemplo concreto:
39 | ____
40 | Cuando añadimos el artículo `art.culo/interesante.html`, la lista de artículos por leer permanece vacía.
41 | ____
42 |
43 | Las pruebas funcionales, si bien no tienen necesariamente que serlo, se llevan a cabo con frecuencia como pruebas de punta a punta _(end to end)_ (<> pág 10), es decir, pruebas en las que se ejercita la aplicación en su conjunto y desde afuera, simulando las acciones del usuario y de los sistemas colaboradores, y evaluando la corrección según la perciben usuario y colaboradores.
44 |
45 | Hemos decidido resolver el problema propuesto con una aplicación web, _neverread_. Implementaremos en Java una prueba funcional de punta a punta que verifique el criterio de aceptación con una prueba _junit_ que va a iniciar la aplicación y a continuación ejercitarla y evaluarla. Para simular la interacción de un usuario a través de un navegador web utilizaremos Selenium/WebDriver/HtmlUnit:
46 |
47 | #SNIPPET "purejava/ListStaysEmptyTest.java" active-documentation/src/test-acceptance/purejava/ListStaysEmptyTest.java 15 40#
48 |
49 | Documentación activa
50 | -------------------
51 |
52 | Si bien podríamos considerar que nuestra prueba funcional en Java es la documentación principal donde se registran los criterios de aceptación; dependiendo de quién vaya a consumirla, esto puede o no ser una opción. Además, conseguir reemplazar la legibilidad de un párrafo en español con una prueba en el lenguaje de programación es todo un reto, particularmente si el lenguaje de programación es tan prolijo como Java.
53 |
54 | En consecuencia, en la mayoría de los proyectos es necesario documentar los requisitos de manera más textual.
55 |
56 | Una forma de intentar obtener lo mejor de las dos alternativas es conectar la documentación a la aplicación de tal forma que en todo momento sea automático y evidente verificar el cumplimiento de cada uno de los requisitos expresados, lo que nos ayudará a mantener la documentación sincronizada con la aplicación y con el cliente.
57 |
58 | La documentación, junto con la capacidad de procesarla para su verificación, servirá así de batería de pruebas funcionales; si ejecutamos esta batería con frecuencia (idealmente con cada cambio) y la mantenemos veraz, estaremos garantizando el correcto funcionamiento de la aplicación... para la definición especificada.
59 |
60 |
61 | Concordion
62 | ----------
63 |
64 | Concordion es una de las herramientas que nos pueden ayudar a conectar la documentación con la aplicación; en Concordion los criterios se escriben en HTML al que se añaden algunas marcas que comienzan con `concordion:`
65 |
66 | #SNIPPET "concordion/v1/ListStaysEmpty.html" active-documentation/src/test-acceptance/concordion/v1/ListStaysEmpty.html 8 21#
67 |
68 | Vemos que hemos expresado el ejemplo en forma de condición y consecuencia que se debe verificar. Los atributos `concordion:set` y `concordion:assertEquals` conectan el documento al método de la clase de respaldo, escrita en Java, `String articleListAfterAdding(String url)`, que se encargará de hacer lo que dice el texto.
69 |
70 | #SNIPPET "purejava/ListStaysEmptyTest.java" active-documentation/src/test-acceptance/concordion/v1/ListStaysEmptyTest.java 15 38#
71 |
72 | Al ejecutar `ListaPermaneceVaciaTest`, Concordion generará un documento HTML con el texto anterior en el que se indicará si se cumple la aserción resaltándola en verde o rojo.
73 |
74 |
75 | Paso a paso
76 | ~~~~~~~~~~~
77 |
78 | Veamos qué es lo que está pasando aquí. Hemos escrito el criterio de aceptación en `ListaPermaneceVacia.html`. Acompañando al HTML hemos escrito una clase en Java que extiende una clase de infrastructura de Concordion: `class ListaPermaneceVaciaTest extends ConcordionTestCase`.
79 |
80 | Cuando ejecutamos `ListaPermaneceVaciaTest`:
81 |
82 | . Concordion procesa el HTML.
83 | . Concordion detecta la marca `concordion:set="#url"` y guarda el contenido de esa marca HTML (en este caso, «art.culo/interesante.html») en la variable de Concordion `#url`.
84 | . Concordion detecta la marca `concordion:assertEquals="articleListAfterAdding(#url)"`, por lo que busca en la clase de acompañamiento un método denominado `articleListAfterAdding` y lo ejecuta, pasándole el contenido de `#url` como parámetro.
85 | . El método `articleListAfterAdding` simula la acción de un usuario que introduce `url` y obtiene la lista de artículos resultante.
86 | . Mediante `convertListOfArticlesToString`, transformamos la lista producida por WebDriver en una representación textual que pueda ser comparada con el texto del HTML. Hemos decidido que la representación textual de una lista vacía sea «vacía».
87 | . El método `articleListAfterAdding` retorna, devolviendo una cadena (en este caso «vacía») que es comparada con el contenido de la marca HTML en el que se encontró `concordion:assertEquals`.
88 | . Concordion termina de procesar el documento HTML y genera otro HTML en el que el texto que tiene la marca `concordion:assertEquals` está resaltado en verde, para indicar que la aserción se cumple.
89 |
90 |
91 | Manteniendo el nivel de abstracción apropiado
92 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93 |
94 | Es importante esforzarse en describir el funcionamiento de la aplicación en términos del dominio. Por ejemplo, podríamos haber caído en la tentación de escribir el ejemplo como _Cuando el usuario entra una cadena en la caja de texto y pulsa enter, la lista de artículos está vacía_. Sin embargo, eso sería perjudicial porque nos alejaría de la persona que define lo que debe hacer la aplicación y resultaría más _frágil_, es decir, en cuanto decidiéramos cambiar la implementación, por ejemplo, supongamos que las direcciones se introducen arrastrándolas a una zona de la aplicación, tendríamos que reescribir el documento.
95 |
96 | A poco que la documentación activa crezca, las clases de respaldo van a necesitar una cantidad importante de código. Algunas abstracciones pueden ayudarnos reducir la repetición y la fragilidad de las clases de respaldo.
97 |
98 | Podemos hacer que la clase de respaldo sólo _hable_ en el lenguaje del dominio, para lo cual hemos de desarrollar un lenguaje dedicado, con lo que el método quedaría algo así:
99 |
100 | #SNIPPET "concordion/v2_appdriver/ListStaysEmptyTest.java" active-documentation/src/test-acceptance/concordion/v2_appdriver/ListStaysEmptyTest.java 18 21#
101 |
102 | Otra posibilidad es abstraer la página web en términos de los elementos del entorno gráfico, es decir, que hable de elementos visuales de la página.
103 |
104 | #SNIPPET "concordion/v3_pagedriver/ListaStaysEmptyTest.java" active-documentation/src/test-acceptance/concordion/v3_pagedriver/ListStaysEmptyTest.java 18 23#
105 |
106 | La capa de abstracción en términos de lenguaje del dominio es la opción más _pura_, pero dependiendo del proyecto podremos preferir una capa que se exprese en términos gráficos o ambas, dependiendo de la complejidad del proyecto y de cuán involucrado esté el cliente en los detalles gráficos.
107 |
108 |
109 | Pruebas asíncronas
110 | ------------------
111 |
112 | En las secciones anteriores nos hemos permitido hacer un poco _trampas_ que deberíamos descubrir antes de cerrar el artículo. Supongamos que el desarrollador, al escribir el código de la aplicación comete una equivocación por no entender debidamente lo que necesita el cliente; decide hacer una aplicación que _añade_ los artículos a una lista de artículos a leer. Nuestras pruebas funcionales deberían detectar este error marcando la aserción en rojo. Sin embargo, nos encontramos con que la pruebas pasan.
113 |
114 | Evidentemente, nuestras pruebas funcionales no son correctas, esto se debe a que estamos verificando que el estado de la lista de artículos es el mismo después de entrar el nuevo artículo que antes de entrarlo, y la prueba verifica la condición antes de que la aplicación tenga tiempo de añadir erróneamente artículos a la lista.
115 |
116 | Probar sistemas asíncronos es lo suficientemente complejo como para justificar un artículo en sí mismo, pero si enumeramos algunas de las opciones, de más rápidas y sencillas de implementar a menos, tenemos:
117 |
118 | . Probamos sólo la parte síncrona del sistema. Esto hace las pruebas más sencillas y rápidas a costa de reducir el alcance.
119 | . Introducimos puntos de sincronización. Volveremos a este en un segundo.
120 | . Verificamos periódicamente la aserción hasta que se cumpla o se agote el tiempo de espera. En esta opción es crucial ajustar la duración, si esperamos demasiado las pruebas tardarán demasiado innecesariamente, si esperamos demasiado poco tendremos falsos negativos.
121 |
122 | En nuestro ejemplo sabemos que, cada vez que la aplicación responde a la entrada de un nuevo artículo, lo último que hace es borrar la caja de texto. Por lo tanto, podemos utilizar este evento como punto de sincronización, es decir, antes de verificar que la lista permanece vacía esperaremos a que la caja se haya borrado.
123 |
124 |
125 | #SNIPPET "concordion/v5_with_synchronisation/tools/NeverReadDriver.java" active-documentation/src/test-acceptance/concordion/v5_with_synchronisation/tools/NeverReadDriver.java 25 38#
126 |
127 |
128 | Conclusión
129 | ----------
130 |
131 | La _documentación activa_ es una forma de probar funcionalmente un programa en el que cada criterio de aceptación se enuncia con un texto que se enlaza a la ejecución de código que verifica el criterio. Al ejecutarla, se produce un resultado que indica, de forma legible para los expertos del dominio, qué criterios de aceptación cumple el programa y qué criterios no cumple. Como casi todo, su uso debería adaptarse a la composición del equipo y la complejidad del proyecto.
132 |
--------------------------------------------------------------------------------
/extras/herramientas.asciidoc:
--------------------------------------------------------------------------------
1 | Aprende tu editor a fondo
2 | =========================
3 | Esteban Manchado_Velázquez
4 |
5 | Conocer las herramientas es importante. Este artículo se centrará en los
6 | aspectos más importantes de aprender a usar un editor de texto bien, dando
7 | ejemplos para los dos grandes «clásicos» de edición de texto para
8 | programadores: VIM y Emacs.
9 |
10 |
11 | «El mejor editor»
12 | -----------------
13 | Siempre ha habido y habrá discusiones sobre qué herramientas son mejores o
14 | peores. No hay respuesta única, cada persona tiene un estilo y unas necesidades
15 | diferentes. Lo importante no es encontrar «la mejor», sino escoger una buena
16 | herramienta y aprenderla bien.
17 |
18 | Dos editores populares, especialmente en el mundo Unix, son VIM y Emacs. El
19 | primero es pequeño y está disponible en cualquier instalación de Unix (si no
20 | VIM, al menos algún otro clon de vi). Su punto fuerte es ser un editor pequeño,
21 | rápido y ágil. Cuesta un poco aprenderlo y acostumbrarse a sus diferentes
22 | modos, pero una vez se aprende se puede usar en cualquier sitio.
23 |
24 | Por el contrario, Emacs es bastante grande, muy completo y lleno de
25 | extensiones. Tarda más en arrancar, pero es prácticamente un sistema operativo.
26 | Sus usuarios más devotos lo hacen prácticamente todo desde Emacs: editar texto
27 | (incluso ficheros en otras máquinas), leer el correo, navegar páginas web,
28 | escribir planes, mapas mentales, gestionar listas de tareas, etc. Todo esto se
29 | consigue mediante los llamados «modos» de Emacs, que implementan extensiones,
30 | adaptaciones y mejoras para diferentes actividades o tipos de fichero a editar.
31 | El punto fuerte de Emacs es, sin duda, la programabilidad y las extensiones que
32 | sus usuarios escriben y comparten. No es demasiado cómodo de usar con la
33 | configuración de paquete, por lo que los usuarios tienden a personalizarlo
34 | _mucho_. Esto hace que, una vez se aprende, uno sabe usar la propia
35 | configuración, pero no necesariamente el Emacs configurado por otra persona.
36 |
37 | Cuando empezamos a usar y aprender VIM o Emacs, lo más probable es que tengamos
38 | que cambiar ligeramente nuestras costumbres y adaptarnos a ellos. Esto no es
39 | necesariamente malo, pero conocer las diferentes opciones y posibilidades de
40 | personalización también es buena idea.
41 |
42 |
43 | Movimiento
44 | ----------
45 | Como programadores, lo más probable es que pasemos una buena parte del día en
46 | nuestro editor. Por tanto, es importante que nuestras manos estén lo más
47 | relajadas posible. Evitar el uso constante del ratón y mantener las manos en
48 | posición de escribir (es decir, evitar el uso de las flechas de movimiento) nos
49 | ayudará a mantener las manos relajadas y a evitar problemas como el túnel
50 | carpiano. Afortunadamente, tanto VIM como Emacs nos permiten movernos sin
51 | necesidad de acudir a las flechas de movimiento. En el caso de VIM, podemos
52 | movernos con las teclas +h+, +j+, +k+, +l+ (izquierda, abajo, arriba y derecha
53 | respectivamente); en el caso de Emacs, con +C-b+, +C-f+, +C-n+ y +C-p+ (por
54 | _backwards_, _forward_, _next_ y _previous_; nótese que, en Emacs, +Ctrl-X+ se
55 | representa como +C-x+, y +Alt-X+ como +M-x+).
56 |
57 | Pero moverse línea a línea, o caracter a caracter, es bastante lento. Por
58 | tanto, aprender a moverse de manera más efectiva es algo de lo que nos podremos
59 | aprovechar todos los días. Y, de nuevo, tanto VIM como Emacs nos ofrecen varias
60 | posibilidades. Entre ellas:
61 |
62 | .Órdenes comunes de movimiento de VIM
63 | [width="80%",options="header"]
64 | |======================
65 | |Orden de VIM |Atajo de Emacs |Significado
66 | |+w+ / +b+ |+M-f+ / +M-b+ |Palabra adelante / palabra atrás
67 | |+{+ / +}+ |+M-{+ / +M-}+ |Bloque anterior / bloque siguiente
68 | |+Ctrl-F+ / +Ctrl-B+ |+C-v+ / +M-v+ |Página siguiente / página anterior
69 | |+H+ / +M+ / +L+ |+M-r+ (cambia entre las tres) |Parte alta / media / baja de la pantalla
70 | |+f+ / +F+ |_No aplicable_ |Ir a la siguiente de la línea / ir a la anterior de la línea
71 | |======================
72 |
73 | Otro consejo, especialmente para Emacs pero aplicable en cualquier caso, es
74 | reasignar la tecla «Bloq. Mayús» como una tercera tecla Control. Esta
75 | configuración hará mucho más cómodo y rápido el usar atajos de teclado con la
76 | tecla «Control», lo cual notaremos a la larga en nuestras manos. Sobre todo con
77 | Emacs.
78 |
79 |
80 | Búsquedas y sustituciones
81 | -------------------------
82 | Las búsquedas y sustituciones son probablemente las características más usadas
83 | de un editor de texto, después de las órdenes de movimiento. De hecho, en Emacs
84 | la búsqueda se usa con frecuencia para moverse por el texto. En VIM hay varias
85 | órdenes de búsqueda, incluyendo buscar la palabra debajo del cursor (con +*+ o
86 | +#+, dependiendo de si buscamos hacia abajo o hacia arriba). En Emacs tenemos
87 | las búsquedas normales en +C-s+ y +C-r+, y varias funciones de búsqueda y
88 | sustitución, incluyendo +replace-regexp+ y +isearch-forward-at-point+.
89 |
90 | Mención aparte merecen las expresiones regulares, muy útiles a la hora de hacer
91 | tanto búsquedas como sustituciones. VIM, Emacs y muchos otros programas
92 | entienden expresiones regulares, lo que junto a su versatilidad las convierte
93 | en una de las herramientas básicas del programador. Por tanto, vale la pena
94 | aprenderlas suficientemente bien como para poder hacer la gran mayoría de las
95 | sustituciones sin tener que consultar el manual. Emacs en particular tiene una
96 | función muy útil para aprender y experimentar con expresiones regulares: la
97 | función +re-builder+.
98 |
99 | Por último, siempre es útil poder encontrar rápidamente la definición de cierta
100 | función, especialmente si no lo conocemos bien o es grande y complejo. Algunos
101 | editores tienen este tipo de navegación integrada. VIM y Emacs permiten este
102 | tipo de navegación con la ayuda de la herramienta externa «ctags».
103 |
104 |
105 |
106 | Sangrado
107 | --------
108 | Una de las características más importantes del formato de nuestro código es el
109 | sangrado. Cualquier editor moderno puede ahorrarnos gran parte del trabajo
110 | de mantener el sangrado correcto. Incluyendo a VIM y Emacs, como podríamos
111 | esperar.
112 |
113 | Para sangrar el código que escribimos automáticamente, VIM ofrece, entre otras,
114 | las opciones +formatoptions+ y +smartindent+. Emacs ofrece diferentes «modos»
115 | para diferentes lenguajes, que se encargan de definir cómo se debería sangrar
116 | cada lenguaje. También es importante tener la posibilidad de reformatear código
117 | ya escrito, operación que podemos hacer con la orden +=+ en VIM y la función
118 | +indent-region+ en Emacs.
119 |
120 | Cuando hablamos de sangrado, la mayoría de los programadores prefiere usar
121 | espacios. Naturalmente, es más cómodo pulsar la tecla Tab que pulsar varias
122 | veces la tecla espaciadora, así que la mayoría de los editores nos permite
123 | pulsar la tecla Tab para sangrar (pero manteniendo espacios para el sangrado).
124 | En VIM usamos la opción +expandtab+; en Emacs, +(setq indent-tabs-mode nil)+.
125 |
126 | Y para los casos en los que necesitemos formatear textos y comentarios (para
127 | que se adapten automáticamente a cierto número de caracteres por línea, digamos
128 | 80), VIM nos ofrece la orden +gq+ y Emacs el atajo +M-q+.
129 |
130 |
131 | Tipos de ficheros
132 | -----------------
133 | Es evidente que tratamos distintos tipos de ficheros de distinta forma:
134 | necesitan unas reglas distintas para el resaltado de sintaxis, para el sangrado
135 | automático, para detectar qué es un comentario, quizás para definir macros,
136 | atajos de teclado especiales o plantillas, etc. La mayoría de los editores
137 | actuales permite, hasta cierto punto, adaptar el entorno a los diferentes tipos
138 | de ficheros.
139 |
140 | Éste es, posiblemente, uno de los puntos más fuertes de Emacs. En Emacs tenemos
141 | los modos, y en VIM tenemos los _filetypes_. La idea de los modos de Emacs
142 | es cambiar el editor para convertirlo en un editor especializado para el tipo
143 | de fichero actual: cada modo tiene su propia configuración, atajos de teclado y
144 | manual, y se aprende más o menos por separado. Una gran parte de la potencia de
145 | Emacs se basa en esto, y la red está llena de modos para todo tipo de tareas,
146 | como Org mode (para planificar, gestionar tareas y proyectos, etc), nXML mode
147 | (para editar y validar XML) o rainbow-mode (para colorear referencias a colores
148 | como #a00 con el color que representan).
149 |
150 | En VIM, la personalización es generalmente mucho menor, y se basa más en
151 | adaptar el sangrado y el resaltado de sintaxis. Sin embargo, nos permite añadir
152 | nuestra propia personalización para cada tipo de fichero, así que siempre
153 | podemos adaptar las opciones activas o cualquier otro aspecto de VIM que
154 | queramos.
155 |
156 |
157 | Macros y atajos de teclado
158 | --------------------------
159 | La programabilidad de un editor es otro aspecto importante que resulta útil
160 | aprender. No necesariamente la programabilidad «pesada» de escribir o modificar
161 | extensiones para el editor, sino la programabilidad de pequeñas macros y atajos
162 | de teclado.
163 |
164 | En VIM, podemos grabar macros con la orden +q+. Esta orden necesita un
165 | parámetro, que será una letra donde guardaremos la macro. Así, si pulsamos
166 | +qa+, empezaremos a grabar una macro en «a». Para terminar, pulsamos +q+, y
167 | para ejecutar una macro, pulsamos +@+ seguida de la letra que queramos, o +@@+
168 | para repetir la última macro _ejecutada_.
169 |
170 | En Emacs, lo normal es hacer una de dos cosas: grabar pequeñas macros en la
171 | sesión, o escribir funciones en Emacs Lisp para hacer pequeñas extensiones de
172 | nuestro editor. Además, las primeras se pueden convertir en las segundas con la
173 | ayuda de las funciones +kmacro-name-last-macro+ y +insert-kbd-macro+. Para
174 | grabar una macro en Emacs, pulsamos +F3+ (o +C-x (+) y para terminar de grabar
175 | pulsamos +F4+ (o +C-x )+). Para ejecutar la última macro grabada, pulsamos +C-x
176 | e+.
177 |
178 | Conclusiones
179 | ------------
180 | Aunque pueda parecer exagerado, herramientas aparentemente simples como un
181 | editor de texto son suficientemente complicadas como para que haya muchas, muy
182 | diferentes y prácticamente imposibles de comparar. Por tanto, no se puede decir
183 | que haya un «mejor editor»: depende de para qué lo usemos, cómo lo usemos, lo
184 | que nos resulte personalmente más cómodo y cuánto hayamos aprendido sobre él.
185 |
186 | Pero la conclusión más importante es que un buen profesional conoce sus
187 | herramientas y las aprende a usar bien. Dedicar tiempo a aprender las
188 | herramientas de trabajo ahorra tiempo a la larga.
189 |
190 |
191 | [bibliography]
192 | Bibliografía
193 | ------------
194 | - [[[viunixworld]]] Walter Alan Zintz 'The Vi/Ex Editor'.
195 | http://www.networkcomputing.com/unixworld/tutorial/009/009.html
196 | - [[[emacswiki]]] El Wiki de Emacs http://emacswiki.org/
197 | - [[[boostvim]]] Vincent Driessen 'How I boosted my Vim'
198 | http://nvie.com/posts/how-i-boosted-my-vim/
199 | - [[[effectiveemacs]]] Steve Yegge 'Effective Emacs'
200 | http://sites.google.com/site/steveyegge2/effective-emacs
201 | - [[[vimcasts]]] 'Vimcasts'
202 | http://vimcasts.org/
203 | - [[[emacsrocks]]] 'Emacs rocks'
204 | http://emacsrocks.com/
205 | - [[[masteringemacs]]] 'Mastering Emacs'
206 | http://www.masteringemacs.org
207 | - [[[regularexpressions]]] Jeffrey E.F. Friedl 'Mastering Regular Expressions'
208 | O'Reilly Media. ISBN 0-596-00289-0
209 | http://shop.oreilly.com/product/9780596002893.do
210 | - [[[emacsrebuilder]]] 're-builder: the Interactive regexp builder'
211 | http://www.masteringemacs.org/articles/2011/04/12/re-builder-interactive-regexp-builder/
212 | - [[[ctags]]] Wikipedia 'Ctags' http://en.wikipedia.org/wiki/Ctags
213 | - [[[vimscripts]]] Vim Scripts http://www.vim.org/scripts/
214 | - [[[emacsorgmode]]] Org mode para Emacs http://orgmode.org/
215 | - [[[emacsnxmlmode]]] nXML mode para Emacs
216 | http://www.thaiopensource.com/nxml-mode/
217 | - [[[emacsrainbowmode]]] Rainbow mode para Emacs
218 | http://julien.danjou.info/rainbow-mode
219 | - [[[introelisp]]] Christian Johansen 'An introduction to Emacs Lisp'.
220 | http://cjohansen.no/an-introduction-to-elisp
221 |
--------------------------------------------------------------------------------
/calidad.asc:
--------------------------------------------------------------------------------
1 | Calidad en software
2 | ===================
3 | _Esteban Manchado Velázquez_
4 |
5 | «Calidad» es una palabra muy usada pero un concepto bastante escurridizo, al
6 | menos a la hora de encontrar maneras fiables de mejorarla o simplemente
7 | mantenerla una vez hemos llegado a un nivel aceptable. Este artículo explorará
8 | qué es la calidad y cómo mejorarla en nuestros proyectos.
9 |
10 | Significado
11 | -----------
12 | El primer problema de la calidad es definirla. Hay muchas definiciones, pero mi
13 | preferida en el contexto de la ingeniería de software es «adecuación al uso».
14 | Uno de los problemas de la definición es que es muy vaga pero, paradójicamente,
15 | el propio hecho de ser tan vaga es lo que la hace útil. La calidad es un asunto
16 | muy complejo, por lo que simplificarlo, lejos de ayudarnos a entender, sólo nos
17 | dará la ilusión de que lo entendemos. Y la ilusión de entendimiento es muy
18 | peligrosa porque hace que nos resistamos al aprendizaje real.
19 |
20 | El segundo problema de la calidad es hacer que todos los interesados compartan
21 | lo que entienden por ésta. Este entendimiento compartido nos ayudará a
22 | centrarnos en los objetivos importantes, que son las necesidades del cliente
23 | del proyecto. Esto no significa que sólo sean importantes los aspectos que el
24 | cliente _menciona_: con frecuencia, los clientes dan por sentadas una serie de
25 | propiedades (eficiencia, fiabilidad, etc), y nuestro trabajo es asegurarnos de
26 | que éstas se cumplen tanto como los requisitos explícitos. Esto es, la calidad
27 | necesita de aspectos técnicos como código rápido o diseño simple, pero éstos
28 | deben estar supeditados a las necesidades y los deseos del cliente, implícitos
29 | y explícitos.
30 |
31 | Cómo mejorar la calidad
32 | -----------------------
33 | Como ya expone el apartado anterior, es imposible dar una «receta mágica» para
34 | mejorar la calidad. De hecho, intentar mejorar la calidad simplemente siguiendo
35 | una lista de pasos es un camino casi seguro hacia el fracaso. No importa qué
36 | pasos sigamos, qué hayamos oído de ellos o quién los haya recomendado: nuestra
37 | única vía para alcanzar un buen nivel de calidad es usar nuestra experiencia y
38 | conocimiento del contexto para decidir qué es lo que nos ayudará en cada
39 | momento. Tenemos que ser conscientes de lo que hacemos y tomar el control de
40 | nuestras decisiones.
41 |
42 | Sin embargo, sí se pueden dar _guías_ para mejorar la calidad. Por ejemplo:
43 | mantener siempre el qué, no el cómo, como la guía de todo lo que hacemos;
44 | mantener el escepticismo y cuestionar cómo hacemos las cosas y por qué; estar
45 | preparado para cambiar cómo trabajamos y luchar contra la «programación de
46 | culto al cargo» footnote:[Hacer las cosas de cierta manera simplemente porque lo
47 | hemos hecho o visto antes, sin entender por qué son así o qué utilidad tienen.
48 | Ver http://en.wikipedia.org/wiki/Cargo_cult_programming[Cargo Cult Programming]
49 | en Wikipedia.]; no creer en la tecnología como el centro de lo que hacemos;
50 | darse cuenta de que lo principal no es hacer código bonito o fácil de entender,
51 | sino resolver problemas footnote:[Los buenos profesionales hacen las dos cosas,
52 | pero es más profesional tener más de lo segundo que más de lo primero.]; no
53 | creer que los problemas tengan una solución única o mejor.
54 |
55 | Esto no quiere decir que las herramientas, técnicas y costumbres no sean
56 | útiles. En absoluto. Lo que sí significa es que éstas nos ayudarán según qué
57 | contextos, según qué proyectos, según qué equipos y según qué clientes, pero
58 | nunca en todos los casos. Los otros artículos de este libro describen algunas
59 | de estas técnicas y herramientas, que son muy útiles y todo buen profesional
60 | debe conocer y dominar, pero uno de los mensajes principales de este artículo
61 | es que conocer y adaptarse al contexto es importante, y que aplicar ciegamente
62 | cualquiera de estas técnicas o herramientas es un error.
63 |
64 | Para ilustrar las ideas de este artículo, y como inspiración para ayudar a
65 | pensar fuera de cánones más académicos, los siguientes apartados muestran una
66 | serie de ejemplos de situaciones junto con sugerencias de posibles soluciones.
67 | Obviamente no puede haber solución única o «correcta» en estos ejemplos, entre
68 | otras razones porque ninguna descripción literaria puede dar toda la
69 | información necesaria para tomar una buena decisión.
70 |
71 | Ejemplo 1: procesos
72 | -------------------
73 | Como regla general, tener procesos y reglas ayuda a los equipos de desarrollo a
74 | trabajar más eficientemente. Por una parte, facilita que nos concentremos en lo
75 | que estamos haciendo y no en el cómo footnote:[Si siempre hacemos ciertas cosas
76 | de la misma manera y ésta funciona razonablemente bien, no tenemos que gastar
77 | tiempo ni energía decidiendo cómo hacerlas.]. Por otra, facilita la
78 | automatización.
79 |
80 | Sin embargo, los procesos y las reglas pueden quedarse obsoletos. Por ejemplo,
81 | digamos que uno o dos miembros del equipo causan problemas con la integración
82 | continua: con frecuencia hacen cambios que hacen fallar a la batería de pruebas
83 | y no tienen disciplina para escribir pruebas. Ante esta situación, decidimos
84 | que cada cambio enviado al repositorio debe contener alguna modificación a
85 | algún fichero de pruebas. La medida funciona más o menos bien, esos miembros
86 | del equipo empiezan a tomarse las pruebas más en serio, y así aumentamos la
87 | productividad del equipo con ella.
88 |
89 | Ahora bien, todas las reglas tienen partes buenas y partes malas, como todo.
90 | Esta medida concreta puede ser molesta cuando simplemente queramos arreglar una
91 | falta de ortografía en un comentario, actualizar la documentación, o
92 | reformatear el código (ya que nuestro cambio no modificaría ningún fichero de
93 | pruebas). Por tanto, si en algún momento esos miembros del equipo se marchan o
94 | llegan al punto de escribir buenas pruebas y no necesitar la anterior medida,
95 | puede que llegue el momento de eliminar esta regla. Mantener y seguir reglas
96 | simplemente porque «siempre han funcionado» es caer en la tentación de seguir
97 | el «culto al cargo».
98 |
99 | Ejemplo 2: automatización de pruebas
100 | ------------------------------------
101 | Una de las constantes en los artículos de este libro es el uso de pruebas
102 | automáticas. Aunque es desde luego una de las prácticas más recomendadas y
103 | útiles, también es verdad que con frecuencia tenemos que trabajar con código
104 | legado (es decir, sin pruebas). En esos casos, ¿qué hacemos?
105 |
106 | Por ejemplo, digamos que nos unimos a un equipo que no tiene cultura de pruebas
107 | automáticas, y vamos retrasados con la siguiente entrega. Una parte concreta
108 | del código está dando bastantes problemas, y las pruebas manuales (la manera en
109 | la que el equipo prueba el código) no están encontrando suficientes fallos, o
110 | no lo suficientemente pronto. Aunque automatizar las pruebas es probablemente
111 | la mejor idea a largo plazo, no hay ninguna regla exenta de excepciones
112 | footnote:[Aunque uno podría decir que «no hay ninguna regla exenta de
113 | excepciones» también tiene excepciones...]. Puede que el equipo no tenga
114 | suficiente experiencia en pruebas automáticas como para que introducirlas
115 | mejore la situación _antes de la entrega_. Puede que las partes críticas del
116 | proyecto en el que trabajamos no sean fáciles de probar de manera fiable con
117 | pruebas automáticas, y empeñarnos en poner énfasis en éstas a toda costa no
118 | ayude al equipo en el contexto de esta entrega. Puede que la tensión de ir
119 | retrasados haga al equipo ser «optimistas» al escribir pruebas, y las pruebas
120 | den un falso sentido de seguridad. Puede que los que toman las decisiones no
121 | estén convencidos, y que a corto plazo no valga la pena gastar tiempo y energía
122 | en convencerlos. Y puede que hayamos convencido a los que toman las decisiones,
123 | pero _el equipo_ no esté convencido, e intentar forzarlos a escribir pruebas
124 | sólo vaya a provocar un bajón de moral y un conjunto de pruebas automáticas de
125 | muy mala calidad.
126 |
127 | Y si por la razón que sea llegamos a la conclusión de que intentar implantar
128 | pruebas automáticas para la próxima entrega no es una buena idea, ¿qué podemos
129 | hacer? Una de las posibilidades es empezar haciendo más efectivo al equipo sin
130 | cambiar radicalmente su filosofía de trabajo. Por ejemplo, puede que tras
131 | analizar la situación descubramos que esa parte del programa es más difícil de
132 | probar de manera manual porque no da suficiente información. Quizás añadir una
133 | opción «escondida» que haga esa parte menos opaca puede ser suficiente hasta la
134 | fecha clave. O simplemente mejorar la comunicación entre los miembros del
135 | equipo. Porque, entre otras cosas, siempre es positivo respetar la manera de
136 | trabajar de un equipo («que siempre ha funcionado»): no sólo mejora las
137 | relaciones entre sus miembros, sino que ayuda a ganarse su respeto y atención,
138 | que serán necesarios más tarde para poder implementar cambios grandes como
139 | desarrollo dirigido por pruebas o simplemente escribir pruebas automáticas. Y
140 | mientras tanto, podemos ir enseñando poco a poco al equipo a automatizar las
141 | pruebas para ir cambiando a un modelo (posiblemente) más escalable.
142 |
143 | Ejemplo 3: especificaciones
144 | ---------------------------
145 | Cuando se desarrolla software normalmente se tienen especificaciones. Pueden
146 | ser formales (un documento) o informales (el conocimiento compartido del
147 | equipo). El objetivo de las especificaciones es poder concentrarse en la
148 | solución técnica sin tener que estar preguntando continuamente a los clientes
149 | o al resto del equipo sobre el enfoque a la hora de resolver el problema. Pero,
150 | de nuevo, las especificaciones son sólo una herramienta, y cumplir más a
151 | rajatabla con las especificaciones no tiene por qué garantizar una mayor
152 | calidad del proyecto una vez completado.
153 |
154 | Digamos que estamos desarrollando el software que conduce un coche automático.
155 | Uno de los requisitos del coche es que pare en los pasos de peatones cuando
156 | detecte que una persona está cruzando. Sin embargo, mucha gente no cruza por
157 | los pasos de peatones, así que el coche sería mucho más seguro si no dependiera
158 | de éstos, sino que pudiera detectar por sí mismo si tiene algo delante que
159 | podría llegar a atropellar. Es decir, las especificaciones son una herramienta
160 | muy útil, pero nunca son el objetivo final del desarrollo de software. Las
161 | personas que escriben las especificaciones cometen errores, las condiciones del
162 | proyecto cambian, etc. Mantener siempre el escepticismo y una visión más
163 | completa de nuestros objetivos, y no dejarnos llevar por el «no es mi trabajo»,
164 | el «no me pagan por esto» o el «yo sólo hago lo que me dicen», es mucho más
165 | importante que cumplir la especificación diligentemente. Dicho de otra manera,
166 | nuestro trabajo no es escribir software que cumple a rajatabla una descripción
167 | textual de un problema. Es hacer software útil y resolver problemas.
168 |
169 | Ejemplo 4: diseño simple
170 | ------------------------
171 | Una parte importante de la calidad es, por supuesto, tener código mantenible.
172 | El código mantenible normalmente se consigue con código legible y un diseño
173 | simple. Sin embargo, y como muchas otras cosas, estos dos aspectos son sólo una
174 | herramienta para conseguir calidad: un código legible y un diseño simple hacen
175 | que el código contenga, de media, menos errores, y éstos serán más fáciles de
176 | detectar y corregir.
177 |
178 | Ahora, ¿qué pasa si en algún momento las necesidades del proyecto chocan con
179 | nuestro (por ahora) diseño simple? La respuesta es obvia: las necesidades del
180 | cliente son el objetivo número uno, y lo demás se tiene que adaptar a éstas.
181 | Intentar adaptar las necesidades del cliente al diseño de la aplicación es, en
182 | la mayoría de los casos, un error. Si para resolver el nuevo problema hacemos
183 | el diseño menos lineal o más complejo, no estamos «haciendo una chapuza porque
184 | el cliente no tiene las ideas claras» o porque «no sabe cómo funciona la
185 | aplicación»: estamos ayudando a resolver un problema real. Si eso implica hacer
186 | una «chapuza» en el código, eso probablemente significa que tenemos que revisar
187 | el diseño de nuestra aplicación. No porque lo hayamos hecho mal desde el
188 | principio, sino porque hemos descubierto nuevos requisitos, o refinado los que
189 | teníamos.
190 |
191 | Conclusiones
192 | ------------
193 | Una conclusión a la que podemos llegar es que la calidad es difícil de
194 | conseguir y de medir, y se necesita experiencia y mucho trabajo para obtenerla.
195 | Pero la conclusión más importante es que _es imposible mejorar la calidad de un
196 | proyecto informático aplicando reglas o metodologías_. Da igual cuánta
197 | experiencia o cuánto conocimiento tenga la persona que las haya formulado,
198 | ningún conjunto de reglas o metodologías puede resolver nuestros problemas si
199 | las aplicamos sin entender lo que hacemos y en qué contexto son útiles.
200 |
--------------------------------------------------------------------------------
/integracion_continua.asc:
--------------------------------------------------------------------------------
1 | Integración continua
2 | ====================
3 | _Yeray Darias Camacho_
4 |
5 | Independientemente de si el equipo de desarrollo sigue una metodología
6 | clásica en cascada o algún tipo de metodología ágil hay un momento decisivo
7 | que determina el éxito del proyecto. Este momento es el despliegue de la
8 | aplicación en los sistemas del cliente, lo que conocemos como sistema de
9 | producción.
10 |
11 | Generalmente este suele ser un momento muy tenso porque es muy raro que todo
12 | funcione a la primera. Si se sigue una metodología en la que predomine el
13 | desarrollo incremental, donde las funcionalidades se van entregando poco a poco,
14 | se minimiza ligeramente el impacto, siempre y cuando al final de cada iteración
15 | se haya desplegado en el sistema de producción real. Pero generalmente sigue
16 | siendo un momento incómodo, se suelen producir errores porque las máquinas
17 | de desarrollo tienen configuraciones diferentes a las máquina de producción, el
18 | rendimiento no es tan bueno porque la base de datos de producción tiene una
19 | cantidad mucho mayor de información, o cualquier otro detalle que no se tuvo en
20 | cuenta durante el desarrollo.
21 |
22 | Para resolver este problema aparece una nueva «filosofía» o práctica
23 | denominada integración continua. Es un modo de desarrollar un poco diferente al
24 | habitual, y que requiere de una serie de buenas prácticas y la aceptación de las
25 | mismas por el equipo de desarrollo. Se ha de convertir en un hábito que se
26 | realice de forma automática, casi sin darnos cuenta.
27 |
28 | La integración continua desde el punto de vista del desarrollador
29 | -----------------------------------------------------------------
30 | La siguiente descripción de un día de trabajo, en un equipo que realiza
31 | integración continua, ayudará a ilustrar el proceso y a comprender los elementos
32 | necesarios para llevarla a cabo.
33 |
34 | Al principio del día lo normal es seleccionar la siguiente tarea más importante
35 | a realizar. En base a la reunión de planificación footnote:[Reunión de
36 | aproximadamente una hora en la que se decide cuáles serán las tareas a incluir
37 | en la siguiente versión de la aplicación.] y a la reunión diaria footnote:[Breve reunión de
38 | seguimiento, diaria, que realiza todo el equipo, donde expone en qué se trabajó
39 | el día anterior, en qué se trabajará hoy y si existen impedimentos para llevar a
40 | cabo alguna de las tareas en ejecución.] siempre existe una lista de tareas
41 | priorizadas a disposición del equipo de desarrollo, por lo que es muy sencillo
42 | saber qué es lo siguiente que debemos hacer. Seleccionamos la tarea en la que
43 | debemos trabajar y volvemos a nuestra mesa de trabajo.
44 |
45 | El primer paso será actualizar el código fuente del que disponemos con la
46 | versión más nueva que exista en el repositorio central. De igual manera, si
47 | fuese la primera vez que vamos a trabajar en un proyecto, no tenemos más que
48 | descargar una copia limpia del repositorio de código y empezar a hacer nuestro
49 | trabajo. A lo largo del día implementaremos la nueva funcionalidad, que debería
50 | ser bastante pequeña como para terminarla en una jornada de trabajo y debería
51 | incluir una serie de tests que verifiquen que posee el comportamiento deseado.
52 | Se puede leer más sobre los tests en otros capítulos del libro, ahora mismo no
53 | ahondaremos en el tema porque existen libros enteros sobre TDD y tests
54 | unitarios.
55 |
56 | Cuando la funcionalidad esté terminada, antes de subir ningún cambio al
57 | repositorio, actualizaremos el código fuente con los cambios de nuestros
58 | compañeros y nos aseguraremos de que la aplicación sigue construyéndose
59 | correctamente y que los tests del proyecto están en verde, es decir que pasan
60 | todos sin ningún problema. Si por el contrario aparece algún error lo
61 | arreglaremos inmediatamente. Nunca, bajo ningún concepto, se debe subir código
62 | al repositorio sin revisar que los tests pasan correctamente y la aplicación se
63 | puede construir sin incidencias. Además, es recomendable acceder a la aplicación
64 | y revisar rápidamente que todo sigue funcionando adecuadamente, ya que por lo
65 | general los tests unitarios no tienen una cobertura del 100%, puesto que ciertos
66 | detalles de infraestructura son más sencillos de probar a mano.
67 |
68 | Una vez hemos realizado todos los pasos anteriores podemos guardar nuestros cambios
69 | en el repositorio de código central, lo que permite que el resto del equipo se
70 | actualice y los tengan disponibles en cuestión de segundos.
71 |
72 | Aunque el proceso de integración continua comenzó desde el momento en el que
73 | empezamos a trabajar en la nueva funcionalidad, el servidor de integración
74 | continua comienza a trabajar cuando subimos nuestros cambios al repositorio de
75 | código. Dicho sistema se descargará una versión nueva del código fuente con todos los
76 | cambios realizados (los nuestros y los de nuestros compañeros), ejecutará los tests y
77 | tras hacer la construcción del proyecto lo desplegará en una «réplica» de la máquina
78 | de producción. Todo de forma totalmente automatizada. El propio servidor de
79 | integración continua podría pasar algunas pruebas extras, como por ejemplo
80 | análisis estático de código, análisis de cobertura, o cualquier otro detalle que
81 | sería muy tedioso pasar en el proceso de desarrollo porque requiere demasiado
82 | tiempo.
83 |
84 | También podría haber ocurrido un error. En ese caso el servidor de integración
85 | continua nos avisaría con algún mensaje en la consola del mismo o simplemente
86 | con el envío de un correo electrónico, que es la opción más común. En ese
87 | caso tendremos que inspeccionar cuál ha sido el error y resolverlo para subir
88 | una nueva versión corregida. Como el error se genera a los pocos minutos, y no
89 | han pasado días ni meses, es muy fácil encontrar dónde está el problema.
90 |
91 | Ventajas de la integración continua
92 | -----------------------------------
93 | La experiencia demuestra que reduce de manera increíble el número de errores en
94 | el producto final. Esta es probablemente la mayor ventaja que aporta, porque
95 | un producto final con pocos o incluso ningún error es a fin de cuentas el
96 | objetivo que todos deseamos como desarrolladores.
97 |
98 | Pero no hay que olvidarse de la otra gran ventaja que aporta esta práctica, la
99 | transparencia del proceso. Como todos trabajamos utilizando el mismo repositorio
100 | de código y la información del servidor de integración es pública, para todo el
101 | equipo e incluso a veces para el cliente, se conoce con precisión el estado real
102 | del proyecto. No hay que esperar durante meses para saber cómo van las cosas y
103 | las reuniones de seguimiento pueden ser cortas y precisas.
104 |
105 | Como ventaja añadida cabe destacar la posibilidad de disponer de un servidor de
106 | demostración en el que siempre se posee la última versión de la aplicación. De
107 | esta manera los clientes pueden probar los últimos cambios día a día y ofrecer
108 | información al equipo de desarrollo sin tener que esperar meses.
109 |
110 | Requisitos de la integración continua
111 | -------------------------------------
112 | Ahora ya disponemos de una visión más concreta de lo que es la integración
113 | continua, pero nos vendrá bien repasar cuáles son los elementos indispensables
114 | para hacerlo correctamente.
115 |
116 | Repositorio de código
117 | ~~~~~~~~~~~~~~~~~~~~~
118 |
119 | El repositorio de código es fundamental la herramienta que permite que el equipo
120 | trabaje de forma totalmente sincronizada, pero a la vez sencilla. Cada uno trabaja
121 | en su parte y puede actualizar los cambios sin necesidad de que otros
122 | desarrolladores tengan que esperar por estos y viceversa.
123 |
124 | Desgraciadamente todavía hay muchas empresas que aún no poseen una herramienta
125 | de este tipo. Para aquellos que aún no utilicen un repositorio de código cabe
126 | destacar que actualmente existen una gran cantidad de soluciones gratuitas de
127 | calidad, como pueden ser Subversion, Git o Mercurial por mencionar algunas. Esta
128 | es una pieza fundamental y será prácticamente imposible realizar integración
129 | continua si no se dispone de ella.
130 |
131 | Este tipo de herramientas marcan un antes y un después en el trabajo cotidiano
132 | de un equipo de desarrollo de software. Pero además es el pivote fundamental de
133 | la integración continua, que no es posible sin un repositorio de código.
134 |
135 | Sistema de construcción automatizado
136 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
137 |
138 | Muchos equipos utilizan complejos entornos de desarrollo para implementar y compilar
139 | los proyectos. Esto en sí mismo no es malo, pero debemos poder construir el proyecto
140 | en cualquier máquina sin necesidad de dichos entornos. No todas las máquinas en las
141 | que vayamos a instalar nuestro proyecto tendrán el entorno de desarrollo con el que
142 | trabajamos, incluso en algunos casos estos pueden tener licencias relativamente
143 | caras, por lo que no sería una opción viable.
144 |
145 | Hay muchas soluciones de construcción diferentes en función de los lenguajes
146 | de programación, en Java están Ant o Maven, en Ruby está Rake, solo por
147 | mencionar algunas. La condición es que debe ser una herramienta muy sencilla que
148 | se pueda ejecutar en cualquier máquina y que consiga automatizar no solo el
149 | proceso de compilación, sino la ejecución de los tests de nuestra aplicación e
150 | incluso el despliegue en el servidor de aplicaciones llegado el caso.
151 |
152 | Commits diarios
153 | ~~~~~~~~~~~~~~~
154 |
155 | No todos los requisitos se basan en tener un determinado tipo de herramienta,
156 | algunos se basan en saber cómo utilizar una determinada herramienta. Una vez que
157 | tenemos un control de versiones hay que recordar que el almacenamiento de los
158 | cambios debe ser diario, idealmente un desarrollador debe hacer incluso varias
159 | subidas al repositorio de código al día.
160 |
161 | Si tenemos un repositorio de código, pero cada desarrollador sube sus cambios
162 | cada dos semanas seguimos en el mismo problema de antes, la integración del
163 | proyecto se retrasa durante todo ese tiempo, perdiendo toda opción de obtener
164 | información inmediatamente. Además, retrasar la integración tanto tiempo genera más
165 | conflictos entre los ficheros editados por los desarrolladores. Debido a que con cada
166 | actualización del repositorio se hace una integración del proyecto completo, cada
167 | mínimo cambio que no desestabilice el proyecto debe ser subido para que se compruebe
168 | que todo sigue funcionando correctamente.
169 |
170 | Pruebas unitarias
171 | ~~~~~~~~~~~~~~~~~
172 |
173 | Hemos hablado todo el tiempo de comprobar que el proyecto funciona correctamente
174 | y al mismo tiempo de automatizar todo el proceso lo mejor posible. Para poder
175 | comprobar que el proyecto funciona de forma automática necesitamos pruebas
176 | unitarias (muchas veces incluso pruebas de integración).
177 |
178 | Existen muchas maneras diferentes de incluir tests en el proyecto, personalmente
179 | creo que TDD es la manera más adecuada, pero si el equipo no tiene experiencia y
180 | crea las pruebas después de haber realizado la implementación tampoco es un
181 | método inválido. La premisa fundamental es que toda nueva funcionalidad debe
182 | tener una batería de pruebas que verifique que su comportamiento es correcto.
183 |
184 | Servidor de integración
185 | ~~~~~~~~~~~~~~~~~~~~~~~
186 |
187 | Esta es la pieza más polémica, mucha gente cree que no es absolutamente
188 | necesaria para hacer integración continua correctamente, por ejemplo, algunos
189 | equipos hacen la integración de forma manual con cada subida al repositorio de código.
190 | En mi opinión es un paso tan sencillo y barato de automatizar que no merece la pena
191 | ahorrárselo. Montar algún servidor de integración, como por ejemplo Jenkins o Cruise
192 | Control, es gratuito y tan sencillo como desplegar un fichero en un servidor. Por
193 | contra las ventajas son grandísimas. Para empezar el proceso está totalmente
194 | automatizado, lo que evita el error humano. Y por otro lado reduce la cantidad
195 | de trabajo, ya que tenemos una solución prefabricada sin necesidad de tener que
196 | crear nosotros mismos complejas soluciones caseras.
197 |
198 | Un paso más allá
199 | ----------------
200 | Con los pasos descritos hasta el momento ya tendríamos un proceso bastante
201 | completo y que a buen seguro mejorará enormemente la calidad de nuestro
202 | producto. Pero podemos ir un poco más allá y utilizar el servidor de integración
203 | para que haga ciertas tareas relativamente complejas por nosotros.
204 |
205 | Por ejemplo, podríamos configurar el servidor para que haga análisis estático de
206 | código, de forma que pueda buscar en todo el código bloques sospechosos,
207 | bloques duplicados o referencias que no se utilizan, entre otras cosas. Existen
208 | gran cantidad de opciones como pueden ser FindBugs o PDM. A simple vista puede
209 | parecer algo irrelevante, pero hay que recordar que la complejidad es lo que más
210 | ralentiza el proceso de desarrollo, por lo que un código con menor número de
211 | líneas y referencias inútiles será más sencillo de leer y entender.
212 |
213 | También podríamos incluir tareas que permitan desplegar la aplicación en un
214 | servidor, de forma que tendríamos siempre un servidor de demostración con la
215 | última versión de nuestro proyecto. Esto es realmente útil cuando tenemos un
216 | cliente comprometido, dispuesto a probar todos los nuevos cambios y a dar información
217 | al equipo.
218 |
219 | Otra operación que podemos automatizar, y que el servidor de integración podría
220 | hacer por nosotros, es la realización de pruebas de sistema sobre la aplicación.
221 | Imaginemos que estamos desarrollando una aplicación web, podríamos crear tests
222 | con alguna herramienta de grabación de la navegación, como por ejemplo Selenium,
223 | y lanzarlos con cada construcción que haga el servidor. Es un tipo de prueba que
224 | requiere mucho tiempo y no sería viable que se lancen con cada compilación del
225 | desarrollador, pero para el servidor de integración no habría ningún problema.
226 | Este es solo un ejemplo más de la cantidad de cosas que puede hacer un servidor
227 | de integración continua por nosotros, y que nos ayudará a mantener un producto
228 | estable y testeado de manera totalmente automática.
229 |
230 | Para acabar me gustaría utilizar algunos comentarios escuchados por Martin
231 | Fowler cuando habla de integración continua con otros desarrolladores. La
232 | primera reacción suele ser algo como «eso no puede funcionar (aquí)» o «hacer
233 | eso no cambiará mucho las cosas», pero muchos equipos se dan cuenta de que es
234 | más fácil de implementar de lo que parece y pasado un tiempo su reacción
235 | cambia a «¿cómo puedes vivir sin eso?». Ahora es tu elección si decides
236 | probarlo o no, pero antes de hacerlo piensa en lo poco que tienes que perder y
237 | lo mucho que puedes ganar.
238 |
--------------------------------------------------------------------------------
/documentacion_activa.asc:
--------------------------------------------------------------------------------
1 | Documentación activa
2 | ====================
3 | _Joaquín Caraballo_
4 |
5 | Si en un extremo están los que definen el problema a resolver y en el otro los que lo resuelven, ¿cómo podemos asegurarnos de que todos estamos intentando resolver el mismo problema? Y, si aceptamos que la definición va a necesitar ser enriquecida, corregida, matizada, e incluso que el problema a resolver va a cambiar radicalmente durante la vida del proyecto, ¿cómo mantenemos una misma definición para todos los interesados, y más importante, cómo aseguramos que nuestro programa resuelve el problema?
6 |
7 |
8 | Las pruebas funcionales
9 | -----------------------
10 |
11 | Para tener una cierta confianza en que la aplicación resuelve un cierto problema se llevan a cabo pruebas funcionales; estas resuelven un ejemplo concreto y representativo simulando las acciones del usuario y verificando que la reacción de la aplicación es la esperada.
12 |
13 | Las pruebas funcionales han de mantenerse al día y evolucionar con los cambios de la aplicación y sus requisitos. Otro de los retos es que sean correctas, es decir, que el comportamiento que estén verificando sea realmente el que se requiere de la aplicación.
14 |
15 | Es crucial que las pruebas funcionales estén automatizadas; lo típico es escribirlas en el mismo lenguaje de programación de la aplicación. Esto nos permite ejecutar un gran número de ellas de forma sistemática y tras cada cambio, con lo que también nos protegerán de que algo que funcionaba deje de hacerlo, es decir, de posibles _regresiones_.
16 |
17 |
18 | neverread
19 | ~~~~~~~~~
20 |
21 | Supongamos que tenemos un cliente con falta de tiempo para leer artículos de Internet.
22 |
23 | ____________________________________________________________________
24 | -Tantos artículos interesantes, no tengo tiempo para leerlos, pero tampoco puedo ignorarlos. Me gustaría tener una aplicación en la que copiar la dirección del artículo que me interese.
25 |
26 | -¡Ah!, ¿quieres una aplicación que te permita almacenar enlaces a artículos? La aplicación mantendría una lista con los artículos almacenados, a la que luego podrías acceder cuando tengas tiempo y leer los artículos...
27 |
28 | -No, no, si yo lo que quiero es que los enlaces a artículos desaparezcan, si tuviera tiempo para leerlos después usaría instapaper, hombre. Estaría bien que tuviese una lista de artículos por leer siempre vacía, me daría una sensación genial de tenerlo todo bajo control.
29 |
30 | -Er... vale.
31 | ____________________________________________________________________
32 |
33 | De la conversación hemos conseguido extraer un criterio de aceptación:
34 | ____
35 | Por mucho que añadamos artículos, estos no se añadirán a una lista de artículos por leer.
36 | ____
37 |
38 | La prueba funcional correspondiente, verificará que la aplicación cumple el criterio para un ejemplo concreto:
39 | ____
40 | Cuando añadimos el artículo `art.culo/interesante.html`, la lista de artículos por leer permanece vacía.
41 | ____
42 |
43 | Las pruebas funcionales, si bien no tienen necesariamente que serlo, se llevan a cabo con frecuencia como pruebas de punta a punta _(end to end)_ (<> pág 10), es decir, pruebas en las que se ejercita la aplicación en su conjunto y desde afuera, simulando las acciones del usuario y de los sistemas colaboradores, y evaluando la corrección según la perciben usuario y colaboradores.
44 |
45 | Hemos decidido resolver el problema propuesto con una aplicación web, _neverread_. Implementaremos en Java una prueba funcional de punta a punta que verifique el criterio de aceptación con una prueba _junit_ que va a iniciar la aplicación y a continuación ejercitarla y evaluarla. Para simular la interacción de un usuario a través de un navegador web utilizaremos Selenium/WebDriver/HtmlUnit:
46 |
47 | .Prueba punta a punta en Java (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/purejava/ListStaysEmptyTest.java[purejava/ListStaysEmptyTest.java])
48 | [source,java]
49 | -----------------------------------------------------------------------------
50 | public class ListStaysEmptyTest {
51 | private WebDriver webDriver;
52 | private NeverReadServer neverread;
53 |
54 | @Test
55 | public void articleListStaysEmptyWhenAddingNewArticle() {
56 | webDriver.findElement(By.name("url")).sendKeys("interesting/article.html", Keys.ENTER);
57 | assertThat(webDriver.findElements(By.cssSelector("li")).size(), is(0));
58 | }
59 |
60 | @Before
61 | public void setUp() throws Exception {
62 | neverread = new NeverReadServer();
63 | neverread.start(8081);
64 |
65 | WebDriver driver = new HtmlUnitDriver();
66 | driver.get("http://localhost:8081");
67 | webDriver = driver;
68 | }
69 |
70 | @After
71 | public void tearDown() throws Exception {
72 | webDriver.close();
73 | neverread.stop();
74 | }
75 | }
76 | -----------------------------------------------------------------------------
77 |
78 | Documentación activa
79 | -------------------
80 |
81 | Si bien podríamos considerar que nuestra prueba funcional en Java es la documentación principal donde se registran los criterios de aceptación; dependiendo de quién vaya a consumirla, esto puede o no ser una opción. Además, conseguir reemplazar la legibilidad de un párrafo en español con una prueba en el lenguaje de programación es todo un reto, particularmente si el lenguaje de programación es tan prolijo como Java.
82 |
83 | En consecuencia, en la mayoría de los proyectos es necesario documentar los requisitos de manera más textual.
84 |
85 | Una forma de intentar obtener lo mejor de las dos alternativas es conectar la documentación a la aplicación de tal forma que en todo momento sea automático y evidente verificar el cumplimiento de cada uno de los requisitos expresados, lo que nos ayudará a mantener la documentación sincronizada con la aplicación y con el cliente.
86 |
87 | La documentación, junto con la capacidad de procesarla para su verificación, servirá así de batería de pruebas funcionales; si ejecutamos esta batería con frecuencia (idealmente con cada cambio) y la mantenemos veraz, estaremos garantizando el correcto funcionamiento de la aplicación... para la definición especificada.
88 |
89 |
90 | Concordion
91 | ----------
92 |
93 | Concordion es una de las herramientas que nos pueden ayudar a conectar la documentación con la aplicación; en Concordion los criterios se escriben en HTML al que se añaden algunas marcas que comienzan con `concordion:`
94 |
95 | .Primera prueba usando Concordion (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/concordion/v1/ListStaysEmpty.html[concordion/v1/ListStaysEmpty.html])
96 | [source,html]
97 | -----------------------------------------------------------------------------
98 |
99 | When an article is added, the list of unread articles stays empty
100 |
101 |
102 |
103 | Example
104 |
105 |
106 | When the article
107 | interesting/article.html
108 | is added, the list of unread articles stays
109 | empty
110 |
111 |
112 | -----------------------------------------------------------------------------
113 |
114 | Vemos que hemos expresado el ejemplo en forma de condición y consecuencia que se debe verificar. Los atributos `concordion:set` y `concordion:assertEquals` conectan el documento al método de la clase de respaldo, escrita en Java, `String articleListAfterAdding(String url)`, que se encargará de hacer lo que dice el texto.
115 |
116 | .Clase de respaldo de la prueba en Concordion (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/concordion/v1/ListStaysEmptyTest.java[purejava/ListStaysEmptyTest.java])
117 | [source,java]
118 | -----------------------------------------------------------------------------
119 | public class ListStaysEmptyTest extends ConcordionTestCase {
120 | private WebDriver webDriver;
121 | private NeverReadServer neverread;
122 |
123 | @SuppressWarnings(value = "unused")
124 | public String articleListAfterAdding(String url) throws InterruptedException {
125 | webDriver.findElement(By.name("url")).sendKeys(url, Keys.ENTER);
126 | List pendingArticles = webDriver.findElements(By.cssSelector("li"));
127 |
128 | return convertListOfArticlesToString(pendingArticles);
129 | }
130 |
131 | private static String convertListOfArticlesToString(List pendingArticles) {
132 | if (pendingArticles.isEmpty()) return "empty";
133 | else {
134 | StringBuilder stringBuilder = new StringBuilder();
135 | stringBuilder.append(pendingArticles.get(0).getText());
136 |
137 | for (int i = 1; i < pendingArticles.size(); i++)
138 | stringBuilder.append(", ").append(pendingArticles.get(i).getText());
139 |
140 | return stringBuilder.toString();
141 | }
142 | }
143 | -----------------------------------------------------------------------------
144 |
145 | Al ejecutar `ListaPermaneceVaciaTest`, Concordion generará un documento HTML con el texto anterior en el que se indicará si se cumple la aserción resaltándola en verde o rojo.
146 |
147 |
148 | Paso a paso
149 | ~~~~~~~~~~~
150 |
151 | Veamos qué es lo que está pasando aquí. Hemos escrito el criterio de aceptación en `ListaPermaneceVacia.html`. Acompañando al HTML hemos escrito una clase en Java que extiende una clase de infrastructura de Concordion: `class ListaPermaneceVaciaTest extends ConcordionTestCase`.
152 |
153 | Cuando ejecutamos `ListaPermaneceVaciaTest`:
154 |
155 | . Concordion procesa el HTML.
156 | . Concordion detecta la marca `concordion:set="#url"` y guarda el contenido de esa marca HTML (en este caso, «art.culo/interesante.html») en la variable de Concordion `#url`.
157 | . Concordion detecta la marca `concordion:assertEquals="articleListAfterAdding(#url)"`, por lo que busca en la clase de acompañamiento un método denominado `articleListAfterAdding` y lo ejecuta, pasándole el contenido de `#url` como parámetro.
158 | . El método `articleListAfterAdding` simula la acción de un usuario que introduce `url` y obtiene la lista de artículos resultante.
159 | . Mediante `convertListOfArticlesToString`, transformamos la lista producida por WebDriver en una representación textual que pueda ser comparada con el texto del HTML. Hemos decidido que la representación textual de una lista vacía sea «vacía».
160 | . El método `articleListAfterAdding` retorna, devolviendo una cadena (en este caso «vacía») que es comparada con el contenido de la marca HTML en el que se encontró `concordion:assertEquals`.
161 | . Concordion termina de procesar el documento HTML y genera otro HTML en el que el texto que tiene la marca `concordion:assertEquals` está resaltado en verde, para indicar que la aserción se cumple.
162 |
163 |
164 | Manteniendo el nivel de abstracción apropiado
165 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
166 |
167 | Es importante esforzarse en describir el funcionamiento de la aplicación en términos del dominio. Por ejemplo, podríamos haber caído en la tentación de escribir el ejemplo como _Cuando el usuario entra una cadena en la caja de texto y pulsa enter, la lista de artículos está vacía_. Sin embargo, eso sería perjudicial porque nos alejaría de la persona que define lo que debe hacer la aplicación y resultaría más _frágil_, es decir, en cuanto decidiéramos cambiar la implementación, por ejemplo, supongamos que las direcciones se introducen arrastrándolas a una zona de la aplicación, tendríamos que reescribir el documento.
168 |
169 | A poco que la documentación activa crezca, las clases de respaldo van a necesitar una cantidad importante de código. Algunas abstracciones pueden ayudarnos reducir la repetición y la fragilidad de las clases de respaldo.
170 |
171 | Podemos hacer que la clase de respaldo sólo _hable_ en el lenguaje del dominio, para lo cual hemos de desarrollar un lenguaje dedicado, con lo que el método quedaría algo así:
172 |
173 | .Método de respaldo usando lenguaje del dominio (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/concordion/v2_appdriver/ListStaysEmptyTest.java[concordion/v2_appdriver/ListStaysEmptyTest.java])
174 | [source,java]
175 | -----------------------------------------------------------------------------
176 | public String articleListAfterAdding(String article) throws InterruptedException {
177 | driver.addArticle(article);
178 | return convertListOfArticlesToString(driver.getListOfArticles());
179 | }
180 | -----------------------------------------------------------------------------
181 |
182 | Otra posibilidad es abstraer la página web en términos de los elementos del entorno gráfico, es decir, que hable de elementos visuales de la página.
183 |
184 | .Método de respaldo usando lenguaje del entorno gráfico (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/concordion/v3_pagedriver/ListStaysEmptyTest.java[concordion/v3_pagedriver/ListaStaysEmptyTest.java])
185 | [source,java]
186 | -----------------------------------------------------------------------------
187 | public String articleListAfterAdding(String url) throws InterruptedException {
188 | page.enterIntoNewArticlesTextBox(url);
189 | List pendingArticles = page.getArticlesInListOfArticles();
190 |
191 | return convertListOfArticlesToString(pendingArticles);
192 | }
193 | -----------------------------------------------------------------------------
194 |
195 | La capa de abstracción en términos de lenguaje del dominio es la opción más _pura_, pero dependiendo del proyecto podremos preferir una capa que se exprese en términos gráficos o ambas, dependiendo de la complejidad del proyecto y de cuán involucrado esté el cliente en los detalles gráficos.
196 |
197 |
198 | Pruebas asíncronas
199 | ------------------
200 |
201 | En las secciones anteriores nos hemos permitido hacer un poco _trampas_ que deberíamos descubrir antes de cerrar el artículo. Supongamos que el desarrollador, al escribir el código de la aplicación comete una equivocación por no entender debidamente lo que necesita el cliente; decide hacer una aplicación que _añade_ los artículos a una lista de artículos a leer. Nuestras pruebas funcionales deberían detectar este error marcando la aserción en rojo. Sin embargo, nos encontramos con que la pruebas pasan.
202 |
203 | Evidentemente, nuestras pruebas funcionales no son correctas, esto se debe a que estamos verificando que el estado de la lista de artículos es el mismo después de entrar el nuevo artículo que antes de entrarlo, y la prueba verifica la condición antes de que la aplicación tenga tiempo de añadir erróneamente artículos a la lista.
204 |
205 | Probar sistemas asíncronos es lo suficientemente complejo como para justificar un artículo en sí mismo, pero si enumeramos algunas de las opciones, de más rápidas y sencillas de implementar a menos, tenemos:
206 |
207 | . Probamos sólo la parte síncrona del sistema. Esto hace las pruebas más sencillas y rápidas a costa de reducir el alcance.
208 | . Introducimos puntos de sincronización. Volveremos a este en un segundo.
209 | . Verificamos periódicamente la aserción hasta que se cumpla o se agote el tiempo de espera. En esta opción es crucial ajustar la duración, si esperamos demasiado las pruebas tardarán demasiado innecesariamente, si esperamos demasiado poco tendremos falsos negativos.
210 |
211 | En nuestro ejemplo sabemos que, cada vez que la aplicación responde a la entrada de un nuevo artículo, lo último que hace es borrar la caja de texto. Por lo tanto, podemos utilizar este evento como punto de sincronización, es decir, antes de verificar que la lista permanece vacía esperaremos a que la caja se haya borrado.
212 |
213 |
214 | .Ejemplo de punto de sincronización (https://github.com/emanchado/camino-mejor-programador-codigo/blob/master/active-documentation/src/test-acceptance/concordion/v5_with_synchronisation/tools/NeverReadDriver.java[concordion/v5_with_synchronisation/tools/NeverReadDriver.java])
215 | [source,java]
216 | -----------------------------------------------------------------------------
217 | public void addArticle(String url) {
218 | webDriver.findElement(By.name("url")).sendKeys(url, Keys.ENTER);
219 |
220 | new WebDriverWait(webDriver, 2).until(new ExpectedCondition