- download the above mentioned file (http://github.com/saucelabs/saucelenium/blob/master/selenium-sauce.jar)
- rename it to selenium-server.jar
- replace the previous file in the vendor folder of your webrat gem (mine was /usr/local/lib/ruby/gems/1.8/gems/webrat-0.7.0/vendor/selenium-server.jar)
One (and a Half) Useful Thing to Know When Using DeepTest Gem With MySQL
... def grant_privileges(connection) sql = %{grant all on #{worker_database}.* to %s@'localhost';} % [ connection.quote(worker_database_config[:username])# , # connection.quote(worker_database_config[:password]) <-- mysql with no password won't work ] connection.execute sql end ...Another tip (the “half” in the blog post title): Don’t forget to edit the “pattern” option in your DeepTest rake task, to be able to grab all the testcases you want. In my case, I want to skip a whole folder containing selenium tests, so I have to write my Deep Test rake file this way: (in /lib/tasks/test.rake)
require "deep_test/rake_tasks" ... DeepTest::TestTask.new "deep" do |t| t.number_of_workers = 2 t.pattern = "test/{unit,functional,integration}/**/*_test.rb" t.libs << "test" t.worker_listener = "DeepTest::Database::MysqlSetupListener" end
ThinkCode.TV Goes Live!
- The first two lessions on Python, by Marco Beri
- A screencast on MacRuby, by Renzo Borgatti
- The first two lessions on TDD, by Piergiuliano Bossi.
Think Different About Mock Objects!
Because you should use mock objects as far as you can apply TDD, whereas you can design and *discover* interfaces (and roles), and assign responsibility. On the other hand, in front of a third-party library you cannot follow this process, since the code is not under your control, and you cannot modify it.
To verify the correct integration with libraries or external components, which are out of you domain, as well as with integration tests, you may use fakes or stubs (and, by the way, the example in the Uncle Bob’s post is actually a stub, not a “hand-rolled mock”).
- http://www.jmock.org/oopsla2004.pdf (I’m quoting again this paper, because it’s a *really* good starting point to understand this approch to mock objects)
- http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.html (if you like to deepen the “Don’t mock third-party libraries” and “Don’t mock value objects” topics)
- http://www.mockobjects.com/2006/10/tell-dont-ask-and-mock-objects.html
- http://www.mockobjects.com/2006/10/jmock-listening-to-opinionated.html
- http://www.mockobjects.com/2006/09/like-fire-drill.html
- http://www.mockobjects.com/2007/04/test-smell-bloated-constructor.html
- http://www.mockobjects.com/2007/04/test-smell-i-need-to-mock-object-i-cant.html
- http://www.mockobjects.com/files/evolving_an_edsl.ooplsa2006.pdf (yet another paper, very good)
- and if you want to know more about the history of mock objects: http://www.mockobjects.com/2009/09/brief-history-of-mock-objects.html
Ruby: How to Spot Slow Tests in Your Test Suite
- Unit: 317 tests, 803 assertions
- Functional: 245 tests, 686 assertions
- Integration: 50 tests, 218 assertions
module TestTimeTracking class ActiveSupport::TestCase def self.should_track_timing? not(ENV["tracking"].nil?) end setup :mark_test_start_time if should_track_timing? teardown :record_test_duration if should_track_timing? def mark_test_start_time @start_time = Time.now end def record_test_duration File.open("/tmp/test_metrics.csv", "a") do |file| file.puts "#{name().gsub(/,/, '_')},#{Time.now - @start_time}" end end end endThen, edit your “test_helper.rb” (again, under the test folder), to require and include the previous module. E.g. *test_helper.rb*
ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require "test_time_tracking" class ActiveSupport::TestCase include TestTimeTracking ...then, all you have to do is executing your rake task with the “tracking” option set, e.g.
tracking=on rake
At the end of the test suite execution you’ll find a CSV file (test_metrics.csv) in your /tmp folder.
This file contains a line for each test method executed, along with its duration in seconds.
I use to upload this file in google docs, and then apply a formula to sort out the methods from the slowest to the fastest.
A good formula is the following:
=Sort(A2:B612, B2:B612, FALSE)
The main limitation in the current implementation of this module is that every time the suite is executed with rake, the new time metrics collected are appended at the end of the previous file (if it exists), so each time you should remember to move the file to a different location. I’m working on this issue, so I’m expecting to find a better solution. Stay tuned!
Come Fare Integration Test Su Un Plugin Per Jira 3.13.4
- http://confluence.atlassian.com/display/DEVNET/How+to+Build+an+Atlassian+Plugin
- http://confluence.atlassian.com/display/DEVNET/Testing+your+plugin
“atlassian.product.version” - version of the Atlassian product to compile and test against.
“atlassian.product.data.version” - version of the test resource bundle that contains the basic Atlassian product configuration data for the integration test environment. These versions mimic the actual Atlassian product versions. However we might only modify and release the relevant projects for the reasons of non-backwards compatibility of the new versions of Atlassian products. Therefore not every version of Atlassian products will have a corresponding version of the resource bundle.
La property “atlassian.product.test-lib.version” non è documentata, e per capire il suo significato dovete chiedere a Google, che vi rispondera’ con questa utile pagina.“atlassian.product.test-lib.version” - The version of the testing library to use, as a general recommendation you should at least use version 2.0 or higher as it exposes more of the page’s content and provides quite a few extra helper classes to aid in your testing.
Benissimo, quindi io che sto facendo un plugin per la versione 3.13.4 di Jira, sostituisco questo valore nelle tre properties del POM<properties> ... <atlassian.product.version>3.13.4</atlassian.product.version> <atlassian.product.test-lib.version>3.13.4</atlassian.product.test-lib.version> <atlassian.product.data.version>3.13.4</atlassian.product.data.version> </properties>Detto, fatto. Mi manca solo di creare il mio primo test di integrazione, rigorosamente nel package che inizia con “it”.
package it.com.sourcesense.jira.plugin; import com.atlassian.jira.webtests.JIRAWebTest; public class JiraTest extends JIRAWebTest { public JiraTest(String name) { super(name); } public void setUp() { super.setUp(); restoreDataWithLicense("JiraDataForTest.xml", ENTERPRISE_KEY); } public void testVerySimple() throws Exception { assertTextPresent("This JIRA site is for demonstration purposes only"); } }E copiare il dump esportato da Jira per avere qualche dato di test (JiraDataForTest.xml) nel folder src/test/xml/ del progetto del plugin. A questo punto non mi resta che lanciare il seguente comando nella home della progetto
mvn integration-teste aspettare con pazienza che maven scarichi quel Terabyte di jar di cui dichiara di aver bisogno. Primo problema: la console di mvn mi dice
[INFO] [jar:jar] [INFO] Building jar: /private/tmp/HelloWorldPlugin/target/MyWonderfulPluginToSaveTheWorld-1.0-SNAPSHOT.jar [INFO] [antrun:run {execution: generate-integration-test-config}] [INFO] Executing tasks [touch] Creating /private/tmp/MyWonderfulPluginToSaveTheWorld/target/test-classes/localtest.properties [propertyfile] Updating property file: /private/tmp/MyWonderfulPluginToSaveTheWorld/target/test-classes/localtest.properties [INFO] Executed tasks [INFO] [antrun:run {execution: pre-integration-test-user-ant-tasks}] [INFO] Executing tasks [INFO] Executed tasks [INFO] [atlassian-test-harness:start-fisheye {execution: start-fisheye}] [INFO] Skipping fisheye; startService is set to false [INFO] [atlassian-test-harness:start-confluence {execution: start-confluence}] [INFO] Skipping confluence; startService is set to false [INFO] [atlassian-test-harness:start-jira {execution: start-jira}] [INFO] Output log is set to /private/tmp/MyWonderfulPluginToSaveTheWorld/target/jira/output.logE si blocca lì. Vado a vedere il log segnalato nell’ultima riga della console, e scopro una pletora di eccezioni:
2009-07-07 16:11:19,568 main ERROR [com.atlassian.license.LicenseManager] Exception getting license: java.lang.RuntimeException: contactLicense was null at org.picocontainer.defaults.DecoratingComponentAdapter.getComponentInstance(DecoratingComponentAdapter.java:42) at org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentInstance(SynchronizedComponentAdapter.java:35) ...Indago, guardo su Google, niente. Provo allora a sostituire 3.13.4 con 3.13.2 nelle tre properties del POM
<properties> ... <atlassian.product.version>3.13.2</atlassian.product.version> <atlassian.product.test-lib.version>3.13.2</atlassian.product.test-lib.version> <atlassian.product.data.version>3.13.2</atlassian.product.data.version> </properties>E rilancio “mvn integration-test”. Stavolta l’errore è più chiaro: fallisce il ripristino del dump JiraDataForTest.xml nell’istanza di Jira 3.13.2 che viene avviata da maven, perchè la versione del dump è stata fatta con la 3.13.4, una versione successiva alla 3.13.2, e quindi Jira si rifiuta da caricarla. Eccheccavolo. Vi risparmio tutte le combinazioni di numeri di versione che ho provato a mettere nel POM, senza successo, e vado dritto verso la soluzione. Ecco il pom.xml che funziona
<properties> ... <atlassian.product.version>3.13.2</atlassian.product.version> <atlassian.product.test-lib.version>3.13.4</atlassian.product.test-lib.version> <atlassian.product.data.version>3.13.2</atlassian.product.data.version> </properties>L’altra cosa da fare è modificare i dump di Jira che vorrete usare per i vostri test, in modo da far credere a Jira che sta importando una versione compatibile del dump. Per fare questo dovete: 1. Aprire il dump xml di Jira che usate per i test (nel nostro caso JiraDataForTest.xml) 2. Cercare l’occorrenza di questa property
<OSPropertyEntry id="12345" entityName="jira.properties" entityId="1" propertyKey="jira.version.patched" type="5"/>
Per essere sicuri basta che cerchiate la parola “jira.version.patched”
3. Prendere nota dell’id di questa propery (es 12345) e cercare l’occorrenza di una OSPropertyString con lo stesso id<OSPropertyString id="12345" value="354"/>
Ecco, quel valore (354) rappresenta la build version di Jira, che per la 3.13.4 è proprio 354.
4. Sostituire il valore 354 con 335, che è la build versione di Jira 3.13.2 e salvare l’xml 5. Rilanciare il test. Tutto dovrebbe filare liscio ora…$ mvn integration-test ... ... [INFO] [jar:jar] [INFO] Building jar: /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/ target/MyWonderfulPluginToSaveTheWorld-1.0-SNAPSHOT.jar [INFO] [antrun:run {execution: generate-integration-test-config}] [INFO] Executing tasks [propertyfile] Updating property file: /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/ target/test-classes/localtest.properties [INFO] Executed tasks [INFO] [antrun:run {execution: pre-integration-test-user-ant-tasks}] [INFO] Executing tasks [INFO] Executed tasks [INFO] [atlassian-test-harness:start-fisheye {execution: start-fisheye}] [INFO] Skipping fisheye; startService is set to false [INFO] [atlassian-test-harness:start-confluence {execution: start-confluence}] [INFO] Skipping confluence; startService is set to false [INFO] [atlassian-test-harness:start-jira {execution: start-jira}] [INFO] Output log is set to /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/target/jira/output.log [INFO] Finished with jira goal [INFO] [atlassian-test-harness:start-bamboo {execution: start-bamboo}] [INFO] Skipping bamboo; startService is set to false [INFO] [surefire:test {execution: acceptance_tests}] [INFO] Surefire report directory: /Users/pietrodibello/Documents/workspace/MyWonderfulProjectToSaveTheWorld/MyWonderfulPluginToSaveTheWorld/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.com.sourcesense.jira.plugin.JiraTest . . Started it.com.sourcesense.jira.plugin.JiraTest.test. Wed Jul 08 14:30:44 CEST 2009 going to page secure/admin/XmlRestore!default.jspa Asserting text present: Your project has been successfully imported Asserting text present: This JIRA site is for demonstration purposes only . . Finished it.com.sourcesense.jira.plugin.JiraTest.test. Wed Jul 08 14:30:54 CEST 2009 . The test ran in 10.542 seconds . The test suite has been running for 10.536 seconds . Max Mem : 66650112 Total Mem : 2727936 Free Mem : 268968 . ______________________________ Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.045 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 44 seconds [INFO] Finished at: Wed Jul 08 14:30:55 CEST 2009 [INFO] Final Memory: 32M/254M [INFO] ------------------------------------------------------------------------Evviva, barra verde!!
Come Generare Il Report HTML Dei Test Eseguiti Con Jmeter
- la prima riga è sempre raddoppiata, ovvero presente due volte (e vabbè, passi)
- le colonne che indicano i tempi massimi e minimi non sono valorizzate (NaN)”
java org.apache.xalan.xslt.Process -IN jmeterResults.xml -XSL ~/work/jakarta-jmeter-2.3.4/extras/jmeter-results-report_21.xsl -OUT jmeterResults.htmlPurtroppo questa trasformazione non funziona, o meglio, produce un HTML incompleto (vedi sezione “Problema”). Dopo diversi tentativi, ho scoperto la soluzione, grazie ad un commento a questo post: usando la versione 2.4.1 di Xalan-J l’HTML prodotto è completo. Per la cronaca, per lanciare la trasformazione XSL a linea di comando specificando una propria versione di Xalan, basta fare così
java -cp xalan-2.4.1.jar org.apache.xalan.xslt.Process -IN log.jtl -XSL ../extras/jmeter-results-report_21.xsl -OUT foo.htmlE in ant (cosa che interessava me in particolare, perchè lanciavamo jmeter da ant)
<xslt in="${scenario.target.path}/scenario-result.xml" out="${scenario.target.path}/AggregateReport.html" style="${jmeter.home}/extras/jmeter-results-report_21.xsl" classpath="${basedir}/../xalan-2.4.1.jar" />
Sul Coaching…
Caspita, ne è passato di tempo dal mio ultimo post, e tante cose sono cambiate nel frattempo!
La cosa più importante che mi è capitata è che da oramai un anno ho felicemente cambiato lavoro, e grazie a Sourcesense ho avuto la possibilità di continuare a lavorare in un team agile, dopo la passata esperienza nel team XPlayers di Quinary, iniziata nel 2002.
E’ giusto giusto passato un anno da quando lavoro nel team Orione di Sourcesense, e, dopo diversi progetti di sviluppo e di mentoring, ho avuto l’occasione di fare il coach di una piccola parte del team.
Quest’ultima esperienza mi ha portato anche a fare una riflessione più generale sul coaching di un team agile, riflessioni che vorrei cercare di esprimere in senso più o meno compiuto qui di seguito.
E’ vero, verissimo, come spesso si dice e si sente dire, che ogni membro del team dovrebbe avere a cuore il processo e farsi carico di ricordare a tutti (per primo a sè stesso) le pratiche, i principi e i valori alla base del team stesso, soprattutto in condizioni di pressione o difficoltà. E’ quindi ragionevole dire che il team agile *maturo* è un team “senza coach”.
Eppure questa conclusione non mi soddisfa, e non mi convince del tutto.
Io penso che qualunque team, dal più “green” al piu’ navigato ed esperto, abbia bisogno comunque di un coach. Questo perchè ci sono alcuni “ruoli” che, sebbene possano essere interpretati da molti membri del team (da tutti i membri, in un team maturo), devono avere un interprete primario “designato” dal team.
Faccio un esempio: ognuno di noi sa che in caso di conflitti tra membri del team, o di atti di mancanza di rispetto, dovremmo tutti intervenire per riprendere la persona che ha “alzato i toni”, ma il coach, se presente, di certo interverrà per primo.
Stessa cosa se un customer o qualunque persona “esterna” al team di sviluppo cerca di “forzare” il team a lavorare in modi che contrastano con i valori o i principi alla base del team. In tal caso ognuno di noi, indistintamente, dovrebbe intervenire. Ma non si puo’ intervenire tutti, “in mucchio”. Ci vuole una persona che si faccia carico della cosa e che *per prima* protegga il team da interferenze e pressioni esterne, e per me quella persona è il coach.
So che magari a molti risulterà una metafora un pò lontana, ma pensiamo alle squadre sportive (per esempio di calcio). Lì esiste la figura del capitano, che spesso e volentieri non è neanche la persona più brava della squadra (ad es nel Milan(*) il capitano è Pirlo, non Kaka’ o Ronaldinhio), ma che certo interverrà per prima per riprendere un proprio giocatore se si comporta scorrettamente o se non segue gli schemi, o lo difenderà se è vittima di una decisione arbitrale giudicata ingiusta. Ancora, questo non vuol dire che gli altri giocatori non interverrano ugualmente in questi casi, ma di certo il capitano interverrà per primo e tutti i giocatori lo sanno, e quindi da questo punto di vista sono più “tranquilli”.
Per me questo è il ruolo primario e insostituibile del coach: è quello che c’è sempre e per primo. Se poi lui non può esserci, di certo gli altri faranno in modo che la sua assenza non si senta.
Tutto questo lungo e verboso discorso per dire che non penso basti chiedere che “ognuno di noi si prenda cura del processo”, anche se questo sforzo è condizione assolutamente necessaria al buon funzionamento del team.
P.S. Ricordo che nel team Xplayers in Quinary per un certo periodo il buon Piergiuliano Bossi (a cui devo moltissimo), come esercizio a margine degli studi teorici fatti sul libro bianco di XP di Beck, ci faceva, a turno, essere coach del team, per una iterazione. Questo ci serviva per capire che ruolo avesse davvero il coach.
(*) Nota: non sono milanista, è solo per fare un esempio.
Michael Feathers on TDD
The story I heard was that it was all started by John Nolan, the CTO of a startup named Connextra. John Nolan, gave his developers a challenge: write OO code with no getters. Whenever possible, tell another object to do something rather than ask. In the process of doing this, they noticed that their code became supple and easy to change.La frase chiave:
We need practices which help us achieve continuous discipline and a continuous state of reflection. Clean Room and TDD are two practices which, despite their radical differences, force us to think with absolute precision about what we are doing.
[Oracle Tips] Monitorare Le Connessioni Aperte Verso Il Db
select MACHINE, count(*) from V$SESSION group by MACHINE
Per contare solo quelle verso un certo schema
select MACHINE, count(*) from V$SESSION where schemaname = '<NOME DELLO SCHEMA>' group by MACHINE
Per contare solo quelle provenienti da certi client
select MACHINE, count(*) from V$SESSION where upper(machine) like '%<NOME DELLA MACCHINA CLIENT>%' group by MACHINE
Per contare le connessioni aperte verso il db raggruppate per utente
select osuser, count(*) from V$SESSION group by osuser;
Per contare tutte le connessioni aperte (vabbe’, questa e’ banale!)
select count(*) from V$SESSION;
Per vedere anche lo stato della connessione
select count(*), status from V$SESSION group by status;