Blog | Test Driven Development i pro skriptování systému? Ano!

Test Driven Development i pro skriptování systému? Ano!

Chci si napsat takový instalátor Ruckusing migrací. Nic na tom není, stačí upravit několik souborů a bylo by hotovo. To bych se ale nesměl učit Ruby a nesměl bych se rozhodnout, že tu věci napíšu pomocí TDD. Teď vám ukážu pár maličkostí, jak jsem začal.

Nejdřív musme založit repozitář a stáhnout si z GitHubu Ruckusing:

git init ruckusing-installer
cd ruckusing-installer
git clone https://github.com/ruckus/ruckusing-migrations

Založíme si soubory installer, kde bude samotná aplikace a spec, kde budou testy.

touch installer.rb
touch spec.rb

Prádný installer.rb vypadá takhle:

#!/usr/bin/env ruby

class Installer
        def run (args)
        end
end

if __FILE__ == $0
        Installer.new.run ARGV
end
A prázdný spec vypadá takto:
#!/usr/bin/env ruby
require "./installer.rb"
require "rubygems"
require "test/unit"
require "mocha"

describe Installer do
end

Test spustíme příkazem:

rspec spec.rb

Nejdřív chceme otestovat, že bez parametrů vrátí instalátor použití.

Protože installer přímo zapisuje do stdout pomocí puts, musíme nějak odchytit výstup. Tedy obdobu output bufferu z PHP.

V Ruby tohoto dosáhneme pomocí nahrazení $stdout objektu instancí StringIO.

     it "should show usage when no params" do
                install = Installer.new
                $stdout = out = StringIO.new
                install.run []
                out.string.should include("Usage")
                $stdout = STDOUT
        end

Pustíme si test a vidíme, že neprošel.

rspec spec.rb                                       ~/src/ruckusing-integrate
F

Failures:

  1) Installer should show usage when no params
     Failure/Error: out.string.should include("Usage")
       expected "" to include "Usage"
     # ./spec.rb:13:in `block (2 levels) in <top (required)>'

Finished in 0.00118 seconds
1 example, 1 failure

Failed examples:

rspec ./spec.rb:9 # Installer should show usage when no params

Doplníme kód:

     def run (args)
                if args.length == 0
                        print_usage
                end
        end

        private
        def print_usage
                puts "Usage: installer.rb path-to-bin-folder"
        end

A pustíme testy:

rspec spec.rb                                       ~/src/ruckusing-integrate
.

Finished in 0.00114 seconds
1 example, 0 failures

Druhou věc, kterou budem potřebovat, je doplnění shebangu do main.php a generate.php, které budou spouštěné z příkazové řádky přímo.

Shebang bude mít tvar #!/usr/bin/env php

Napíšu si test a do třídy Installer přidám prázdnou metodu prepend_shebang_to_path (path):

     it "should add shebang to file" do
                installer = Installer.new
                path = "./add_shebang_to_me.txt"
                File.open(path, "w") {|f| f.write('^^^ there should it be')}
                installer.prepend_shebang_to path
                File.open(path, "r") do |infile|
                        infile.gets.should include("#!/usr/bin/env php")
                        infile.gets.should include("^^^ there")
                end
        end

A pustím testy a vidím, že selhaly:

rspec spec.rb                                       ~/src/ruckusing-integrate
.F

Failures:

  1) Installer should add shebang to file
     Failure/Error: infile.gets.should include("#!/usr/bin/env php")
       expected "^^^ there should it be" to include "#!/usr/bin/env php"
     # ./spec.rb:23:in `block (3 levels) in <top (required)>'
     # ./spec.rb:22:in `open'
     # ./spec.rb:22:in `block (2 levels) in <top (required)>'

Finished in 0.00717 seconds
2 examples, 1 failure

Failed examples:

rspec ./spec.rb:17 # Installer should add shebang to file

A znovu doplním funkcionalitu:

             newfile = File.new("shebanged-file", "w")
                newfile.write "#!/usr/bin/env php\n"

                oldfile = File.open(path, "r+")
                oldfile.each_line { |line| newfile.puts line}

                oldfile.close
                newfile.close

                File.delete path
                File.rename "shebanged-file", path

Pustím testy:

rspec spec.rb                                       ~/src/ruckusing-integrate
..

Finished in 0.00309 seconds
2 examples, 0 failures

Vidím, že to funguje. No, ale teď je ta pravá chvíle udělat malý refaktoring:

     def prepend_shebang_to (path)
                File.open(path, "r") do |oldfile|
                        File.delete path
                        File.open path, "w" do |newfile|
                                newfile.write "#!/usr/bin/env php\n"
                                newfile.write oldfile.read()
                        end
                end
        end

Další krok bude přidávání executable bitu v právech na main.php a generate.php, pomocí kterých se spouští a generují migrace.

     it "should add executable bit to file" do
                path = "./make_me_executable.txt"
                FileUtils.chmod "u=wr,go=r", path
                installer = Installer.new
                installer.make_file_executable path
                File.executable?(path).should be_true
        end

rspec spec.rb                                       ~/src/ruckusing-integrate
..F

Failures:

  1) Installer should add executable bit to file
     Failure/Error: File.executable?(path).should be_true
       expected: true value
            got: false
     # ./spec.rb:33:in `block (2 levels) in <top (required)>'

Finished in 0.00662 seconds
3 examples, 1 failure

Failed examples:

rspec ./spec.rb:28 # Installer should add executable bit to file

A doplním kód, který projde testy:

def make_file_executable (path)
        FileUtils.chmod "u=rwx,go=rx", path
end

Takhle bych mohl pokračovat pořád dál, až aplikaci dokončím. Pokaždé, když projdou testy, je vhodné udělat commit.

Programování

Předejte zkušenosti i dalším a sdílejte tento článek!



Jiří Knesl
Business & IT konzultant

Jiří Knesl poprvé začal programovat v roce 1993. Od té doby, díky skvělým učitelům a později zákazníkům, měl možnost neustále růst v oboru vývoje webových aplikací a informačních systémů. v roce 2002 se přidal zájem o ekonomii a v roce 2006 o organizaci práce. Vším tím se konstantně profesně zabývá jak ve svém podnikání, tak i u zákazníků. Za posledních 5 let vydal na tato témata přes 400 článků.

Prohlédněte si moje reference

Mám zkušenosti z rozsáhlých projektů pro korporace, velké podniky, střední i malé firmy, ale i pro startupy v cloudu. Zvyšoval jsem jejich know-how, pomáhal nastavovat jejich organizační strukturu, byl lektorem a mentorem v náročných situacích. Podívejte se, jak vidí můj přínos samotní klienti.

Sledujte mé postřehy na sociálních sítích