Continious Testing avec Watchr

Je suis quelqu’un de très fainéant (c’est un problème ?) et je cherche donc toujours à optimiser mes efforts de travail. Taper une combinaison de touche pour lancer des tests, c’est trop long. Je cherchais donc quelque chose de plus efficace.

J’ai découvert Watchr qui est un outils flexible développer en Ruby avec lequel on peut lancer des tests (ou autres chose) en continue et voir les résultats. Watchr est simple d’usage. Il lance un script quand vous sauvegardez un des fichiers qu’il observe.

Exemple de sortie de test - Source : rubyinside.com

Ce sont des pratiques qui proviennent du monde agile et des approches de développement TDD / Red-Green-Refactor. On lance ces tests en continue pour savoir sans attendre si on casse quelque chose. J’ai beaucoup de mal à retrouver ce type d’outils dans d’autres types de projet comme .net et java.

Ma solution a donc été d’utiliser Watchr et d’écrire un script ruby qui prend la forme suivante :

 
watch ("pattern") do |match|
 	do something ... 
end 

Maintenant qu’on a la template de base, je vais vous donner un exemple simple pour F#. L’idée est d’écrire un bout de code dans un fichier .fsx (fichier de script) et de lancer automatique le code.

 
watch ("^.*.fs(x?)$") do |match| # On écoute tous les fichiers .fs et .fsx
 	fsi fichier.fsx		 # On interprète le fichier contenant le script 
end 

Revenons maintenant à mon problème de départ qui est de lancer mes tests Nunit en continue. Pour cela, j’ai cherché comment compiler mes sources à partir du script Ruby. J’ai fini par me tourner vers une solution utilisant Rake + Albacore qui va décrire mon processus de compilation. Rake est un system de build pour Ruby et Albacore une extension développée par Derick Bailey pour MSBuild.

 
require 'albacore' 
#Albacore::log_level = :verbose  
task :default => ['tests:test']  
namespace :tests do
     desc "Build using Albacore"
     msbuild :build do |msb|     
         # path_to_command is optional but I needed to change it for F#
         msb.path_to_command = "C:/Windows/Microsoft.NET/Framework/v4.0.30319/MSBuild.exe"    
         msb.properties :configuration => :Debug
         msb.targets :Build
         msb.solution = "project.sln"
         msb.verbosity = "quiet"
         msb.parameters "/nologo"
    end 
    desc "Run tests with Nunit"
    nunit :test => [:build] do |nunit|
        nunit.path_to_command = "/nunit-console-x86.exe"     
        nunit.assemblies "./project/bin/Debug/project.dll"
        nunit.options '/xml=results.xml'   
    end 
end 

Ce script Rake me permet de compiler mon programme, de lancer les tests avec Nunit ce qui génère un fichier xml avec le résultat des tests. Le script Watchr va ensuite lire le xml pour afficher les tests qui échouent ou un message indiquant que tout va bien.

 
require 'rexml/document'
require 'colored'
begin
  require 'win32console' if RUBY_PLATFORM =~ /w*32/
rescue LoadError
  raise 'You must gem install win32console to use color on Windows'
end

# Run test when a F# file has been modified
watch( '^.*.fs$') do |match|
  system 'cls'

  # Build project and check if build succeded
  build = system 'rake tests:build'
  if build then
    # Run tests
    system 'rake tests:test'
    show_results if File.file? 'results.xml'
  end
end

# Display tests result
# I'm just doing some xml parsing and print to the console
def show_results
  system 'cls'    
  result_file = File.new('results.xml')
  doc = REXML::Document.new result_file

  nb_failure = REXML::XPath.match(doc, '//test-case[@success="False"]').count

  if nb_failure == 0 then
      puts "All tests are green !".on_green.white

      puts
      REXML::XPath.each(doc, '//test-case') do |test|
         puts test.attributes['name'].on_green 
      end
  else
      puts "#{nb_failure} test have failed ! Fix this immediately !".on_red
      REXML::XPath.each(doc, '//failure') do |fail|
          message = fail.elements.each("message"){|m| m}.first.text 
          stack = fail.elements.each("stack-trace"){|m| m}.first.text

          puts
          puts "Message #{message}".on_red
          puts "StackTrace : #{stack}".on_red

      end
  end
end 

Vous pouvez voir ci-dessous des exemples de sortie dans les deux cas, échec et succès.