tdd da un capo all'altro/tdd from end to end
DESCRIPTION
Slides from my presentation at Codemotion 2011 in RomeTRANSCRIPT
![Page 1: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/1.jpg)
Matteo [email protected]
[email protected](cc) Alcuni diritti riservati
TDD da un capo all’altro
1sabato 5 marzo 2011
![Page 2: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/2.jpg)
Introduzione
Quando si parla di Test-Driven Development spesso si sente dire “facciamo TDD sul dominio ma testiamo l'interfaccia utente a mano”. Oppure “vorrei fare TDD
ma non so come applicarlo al database”. In questa presentazione vorrei dare qualche indicazione su
come fare TDD su tutto il sistema, compresa l'interfaccia utente e il database.
L’obiettivo sono i benefici del TDD come strumento di design per tutto il sistema.
2sabato 5 marzo 2011
![Page 3: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/3.jpg)
I miei maestri - Francesco Cirillo
Kent Beck ha già scritto un libro suldesign a oggetti -- è il libro sul TDD
(Workshop Design Emergente 2009)
3sabato 5 marzo 2011
![Page 4: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/4.jpg)
I miei maestri - Carlo Bottiglieri
Quando ho iniziato il TDD non mi avevano detto che serviva solo per il dominio -- così ho imparato a usarlo su tutta
l’infrastruttura :-)
(Italian Agile Day 2010)
4sabato 5 marzo 2011
![Page 5: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/5.jpg)
Tutto parte da Kent Beck
My Starter Test is often at a higher level, more like an application test. ...
The rest of the tests are “Assuming that we receive a string like this...”
5sabato 5 marzo 2011
![Page 6: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/6.jpg)
Main points
• Outside In: inizia dagli input e scava
• One Step: ogni test ti insegna una cosa (e una sola)
6sabato 5 marzo 2011
![Page 7: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/7.jpg)
Scriviamo un blog!
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
7sabato 5 marzo 2011
![Page 8: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/8.jpg)
e. Risponde a HTTP -- test
Blog blog = new Blog(); HttpUser user = new HttpUser();
@Test public void answersToHttp() throws Exception { blog.start(8080); HttpResponse response = user.get("http://localhost:8080/");
assertEquals(HttpResponse.OK, response.getStatus()); blog.shutdown(); }
8sabato 5 marzo 2011
![Page 9: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/9.jpg)
public class Blog {
private Server server; // org.mortbay.jetty.Server
public void start(int port) { server = new Server(port); try { server.addHandler(new JettyAdapter()); server.start(); } catch (Exception e) { throw new RuntimeException(e); } }
private class JettyAdapter extends AbstractHandler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, ... ) { response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write("hello"); ((Request)request).setHandled(true); } }
e. Risponde a HTTP -- impl
9sabato 5 marzo 2011
![Page 10: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/10.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
10sabato 5 marzo 2011
![Page 11: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/11.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
11sabato 5 marzo 2011
![Page 12: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/12.jpg)
f. Produce HTML -- test @Test public void respondsWithHtml() throws Exception { blog.addHandler("/", new Handler() { @Override public HttpResponse handle(HttpRequest request) { return new HttpResponse("<p>Hello</p>"); }}
);
HttpResponse response = user.get("http://localhost:8080/");
assertEquals(HttpResponse.OK, response.getStatus()); assertEquals("<p>Hello</p>", response.getBody()); }
12sabato 5 marzo 2011
![Page 13: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/13.jpg)
public class Blog {
private class JettyAdapter extends AbstractHandler { @Override public void handle(...) { response.setStatus(HttpServletResponse.SC_OK); if (null != handler) { HttpResponse httpResponse = handler.handle(new HttpRequest()); response.getWriter().write(httpResponse.getBody()); } ((Request)request).setHandled(true); } }
private Server server; private Handler handler; // ....
f. Produce HTML -- impl
13sabato 5 marzo 2011
![Page 14: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/14.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
14sabato 5 marzo 2011
![Page 15: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/15.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
15sabato 5 marzo 2011
![Page 16: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/16.jpg)
b. Riceve una lista di blog post dal database -- test
@Test public void returnsPostsFromDatabase() throws Exception { final List<BlogPost> allPosts = asList(new BlogPost(), new BlogPost()); BlogRepository repository = new BlogRepository() { public List<BlogPost> all() { return allPosts; } }; BlogHandler handler = new BlogHandler(repository); HttpResponse response = handler.handle(null); String html = new BlogPage(allPosts).toHtml(); HttpResponse expected = new HttpResponse(html); assertEquals(expected, response); }
public class BlogPost {}
public interface BlogRepository { List<BlogPost> all();}
16sabato 5 marzo 2011
![Page 17: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/17.jpg)
public class BlogHandler implements Handler {
private final BlogRepository repository;
public BlogHandler(BlogRepository repository) { this.repository = repository; }
@Override public HttpResponse handle(HttpRequest request) { List<BlogPost> posts = repository.all(); String body = new BlogPage(posts).toHtml(); HttpResponse response = new HttpResponse(body ); return response; }
}
b. Riceve una lista di blog post dal database -- impl
17sabato 5 marzo 2011
![Page 18: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/18.jpg)
a. Mostra una lista di blog post
b. Mostra una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
18sabato 5 marzo 2011
![Page 19: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/19.jpg)
a. Mostra una lista di blog post
b. Mostra una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
19sabato 5 marzo 2011
![Page 20: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/20.jpg)
a. Mostra una lista di blog post in html
b. Mostra una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
20sabato 5 marzo 2011
![Page 21: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/21.jpg)
a. Mostra una lista di blog post in html -- test
BlogPost firstPost = new BlogPost() {{ put("title", "First p0st!"); put("body", "first body"); }};
BlogPost secondPost = new BlogPost() {{ put("title", "Second p0st!"); put("body", "second body"); }};
@Test public void showsAListOfBlogPosts() throws Exception { List<BlogPost> list = asList(firstPost, secondPost); BlogPage page = new BlogPage(list); String expected = "<div>" + new BlogPostEntry(firstPost).toHtml() + new BlogPostEntry(secondPost).toHtml() + "</div>"; assertDomEquals(expected, page.toHtml()); }
21sabato 5 marzo 2011
![Page 22: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/22.jpg)
La classe BlogPost
public class BlogPost extends HashMap<String, Object> {
}
22sabato 5 marzo 2011
![Page 23: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/23.jpg)
assertDomEquals
public static void assertDomEquals(String message, String expected, String actual) { try { XMLUnit.setControlEntityResolver(ignoreDoctypesResolver()); XMLUnit.setTestEntityResolver(ignoreDoctypesResolver()); XMLUnit.setIgnoreWhitespace(true); XMLAssert.assertXMLEqual(message,
ignoreEntities(expected), ignoreEntities(actual)); } catch (Exception e) { fail(format("Malformed input: '%s'", actual)); } }
http://xmlunit.sourceforge.net/
23sabato 5 marzo 2011
![Page 24: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/24.jpg)
public class BlogPage implements HtmlGenerator { private final List<BlogPost> posts;
public BlogPage(List<BlogPost> posts) { this.posts = posts; }
public String toHtml() { String result = "<div>"; for (BlogPost post : posts) { result += new BlogPostEntry(post).toHtml(); } result += "</div>"; return result; }}
public class BlogPostEntry implements HtmlGenerator { public String toHtml() { return "x"; }}
a. Mostra una lista di blog post in html -- impl
24sabato 5 marzo 2011
![Page 25: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/25.jpg)
BlogPage > BlogPostEntry
@Test public void showsABlogPostEntry() throws Exception { BlogPostEntry entry = new BlogPostEntry(firstPost); String expected = "<div>" + new BlogPostTitle(firstPost).toHtml() + new BlogPostBody(firstPost).toHtml() + "</div>"; assertDomEquals(expected, entry.toHtml()); }
25sabato 5 marzo 2011
![Page 26: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/26.jpg)
public class BlogPostEntry implements HtmlGenerator { private final BlogPost post;
public BlogPostEntry(BlogPost post) { this.post = post; }
public String toHtml() { String result = "<div>" + new BlogPostTitle(post).toHtml() + new BlogPostBody(post).toHtml() + "</div>"; return result; }}
BlogPostEntry > BlogPostTitle, Body
26sabato 5 marzo 2011
![Page 27: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/27.jpg)
@Test public void showsPostTitleInHtml() throws Exception { BlogPostTitle title = new BlogPostTitle(firstPost); String expected = "<h2>First p0st!</h2>"; assertDomEquals(expected, title.toHtml()); }
@Test public void showsPostBodyInHtml() throws Exception { BlogPostBody body = new BlogPostBody(firstPost); assertDomEquals("<div>first body</div>", body.toHtml()); }
BlogPostEntry > BlogPostTitle, Body
27sabato 5 marzo 2011
![Page 28: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/28.jpg)
a. Mostra una lista di blog post in html
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
28sabato 5 marzo 2011
![Page 29: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/29.jpg)
a. Mostra una lista di blog post in html
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
29sabato 5 marzo 2011
![Page 30: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/30.jpg)
Database database = new Database("localhost", "blog_test", "blog_user", "password");
@Test public void selectsOneRow() throws Exception { List<DatabaseRow> rows = database.select("select 2+2"); assertEquals(1, rows.size()); assertEquals(new Long(4), rows.get(0).getLong(0)); }
@Test public void selectsMoreRows() throws Exception { List<DatabaseRow> rows = database.select("(select 2) union (select 3)"); assertEquals(2, rows.size()); assertEquals("2", rows.get(0).getString(0)); assertEquals("3", rows.get(1).getString(0)); }
c. Fa le select sul database -- test
30sabato 5 marzo 2011
![Page 31: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/31.jpg)
c. Fa le select sul database -- impl (che pizza)
public List<DatabaseRow> select(String sql) { List<DatabaseRow> result = new ArrayList<DatabaseRow>(); PreparedStatement statement = null; ResultSet resultSet = null; try { statement = prepareStatement(sql); resultSet = statement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); while (resultSet.next()) { DatabaseRow row = new DatabaseRow(); for (int i = 0; i < metaData.getColumnCount(); i++) { row.put(metaData.getColumnName(i+1), resultSet.getObject(i+1)); } result.add(row); } } catch (Exception e) { throw new RuntimeException(e); } finally { close(resultSet); close(statement); } return result; }
31sabato 5 marzo 2011
![Page 32: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/32.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
32sabato 5 marzo 2011
![Page 33: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/33.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
33sabato 5 marzo 2011
![Page 34: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/34.jpg)
a. Mostra una lista di blog post
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
f. Traduce le righe in BlogPost
34sabato 5 marzo 2011
![Page 35: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/35.jpg)
Database database = new Database( "localhost", "blog_test", "blog_user", "password");
@Test public void getsPostsFromDatabase() throws Exception { MysqlBlogRepository repository = new MysqlBlogRepository(database);
List<BlogPost> all = repository.all(); assertEquals(1, all.size());
BlogPost actual = all.get(0); assertEquals("a title"), actual.get("title")); assertEquals("a body"), actual.get("body")); }
f. Traduce le righe in BlogPost -- test
35sabato 5 marzo 2011
![Page 36: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/36.jpg)
f. Traduce le righe in BlogPost -- test setup
@Before public void setUp() throws Exception { database.execute("delete from blog_posts"); insertBlogPost("a title", "a body"); }
private void insertBlogPost(String title, String body) { String sql = "insert into blog_posts " + "(title, body) values (?, ?)"; database.execute(sql, title, body); }
36sabato 5 marzo 2011
![Page 37: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/37.jpg)
f. Traduce le righe in BlogPost -- impl
public class MysqlBlogRepository implements BlogRepository { private final Database database;
public MysqlBlogRepository(Database database) { this.database = database; }
public List<BlogPost> all() { List<DatabaseRow> rows = database.select("select * from blog_posts"); List<BlogPost> result = new ArrayList<BlogPost>(); for (DatabaseRow row : rows) { BlogPost post = new BlogPost(); post.putAll(row.getMap()); result.add(post); } return result; }}
37sabato 5 marzo 2011
![Page 38: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/38.jpg)
a. Mostra una lista di blog post in html
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
f. Traduce le righe in BlogPost
38sabato 5 marzo 2011
![Page 39: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/39.jpg)
a. Mostra una lista di blog post in html
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
f. Traduce le righe in BlogPost
39sabato 5 marzo 2011
![Page 40: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/40.jpg)
a. Mostra una lista di blog post in html
b. Riceve una lista di blog post dal database
c. Fa le select sul database
d. Produce HTML
e. Risponde a HTTP
f. Traduce le righe in BlogPost
g. Test Drive
40sabato 5 marzo 2011
![Page 41: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/41.jpg)
Insert test data
$ mysql -uroot blog_development
mysql> insert into blog_posts (title, body) values ('The most powerful computer', '...is your heart');Query OK, 1 row affected (0.26 sec)
mysql> insert into blog_posts (title, body) values ('Codemotion 2011', 'TDD rocks!');Query OK, 1 row affected (0.00 sec)
mysql> quitBye$
41sabato 5 marzo 2011
![Page 42: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/42.jpg)
public static void main(String[] args) { Database database = new Database(
"localhost", "blog_development", "blog_user", "password"); BlogRepository repository = new MysqlBlogRepository(database); BlogHandler handler = new BlogHandler(repository);
Blog blog = new Blog(); blog.addHandler("/", handler); blog.start(8080); }
Implement main
42sabato 5 marzo 2011
![Page 43: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/43.jpg)
43sabato 5 marzo 2011
![Page 44: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/44.jpg)
Riferimenti
• Kent Beck, Test-Driven Development, 2003
• Carlo Bottiglieri, Web Apps in TDD, Agile Day 2010
• Matteo Vaccari, TDD per le viste, Agile Day 2010
• Matteo Vaccari, Programmazione web libera dai framework, Webtech Italia 2010
• Miško Hevery, Clean Code Talks
44sabato 5 marzo 2011
![Page 45: TDD da un capo all'altro/TDD from end to end](https://reader034.vdocumenti.com/reader034/viewer/2022052618/554a1148b4c90507558b4d14/html5/thumbnails/45.jpg)
Contattateci per stage o assunzione
Extreme Programming:sviluppo e mentoring
45sabato 5 marzo 2011