Существуют ли какие-либо хорошие решения для отслеживаемости тестирования ruby?

Я пишу Ruby (не Rails) и использую test/unit с shoulda для написания тестов.

Существуют ли какие-либо драгоценные камни, которые позволят мне реализовать прослеживаемость от моих тестов до проектов/требований?

то есть: я хочу пометить свои тесты именем требований, которые они проверяют, а затем создать отчеты о требованиях, которые не проверены или имеют неудачные тесты и т. д.

Надеюсь, это не слишком предприимчиво для рубина.

Спасибо!


person Stewart Johnson    schedule 05.08.2011    source источник


Ответы (2)


Обновление: это решение доступно как гем: http://rubygems.org/gems/test4requirements

Существуют ли какие-либо драгоценные камни, которые позволят мне реализовать прослеживаемость от моих тестов до проектов/требований?

Я не знаю ни одной жемчужины, но ваша потребность послужила вдохновением для небольшого эксперимента, как это можно решить.

  • Вы должны определить свои Требования с помощью RequirementList.new(1,2,3,4)
  • Эти требования можно назначить с помощью requirements в TestCase.
  • каждый тест может быть назначен требованию с помощью requirement
  • после результатов теста вы получите обзор того, какие требования протестированы (успешно)

А теперь пример:

gem 'test-unit'
require 'test/unit'

###########
# This should be a gem
###########   

class Test::Unit::TestCase
  def self.requirements(req)
    @@requirements = req
  end
  def requirement(req)
    raise RuntimeError, "No requirements defined for #{self}" unless defined? @@requirements
    caller.first =~ /:\d+:in `(.*)'/
    @@requirements.add_test(req, "#{self.class}##{$1}")
  end
  alias  :run_test_old :run_test
  def run_test
    run_test_old
    #this code is left if a problem occured.
    #in other words: if we reach this place, then the test was sucesfull
    if defined? @@requirements
      @@requirements.test_successfull("#{self.class}##{@method_name}")
    end
  end
end

class RequirementList
  def initialize( *reqs )
    @requirements = reqs
    @tests = {}
    @success = {}

    #Yes, we need two at_exit.
    #tests are done also at_exit.  With double at_exit, we are after that.
    #Maybe better to be added later.
    at_exit {
      at_exit do 
        self.overview
      end
    }

  end
  def add_test(key, loc)
    #fixme check duplicates
    @tests[key] = loc
  end
  def test_successfull(loc)
    #fixme check duplicates
    @success[loc] = true
  end
  def overview()
    puts "Requirements overiew"
    @requirements.each{|req|
      if @tests[req] #test defined
        if @success[@tests[req]]
          puts "Requirement #{req} was tested in #{@tests[req] }"
        else
          puts "Requirement #{req} was unsuccessfull tested in #{@tests[req] }"
        end
      else
        puts "Requirement #{req} was not tested"
      end
    }
  end
end #RequirementList

###############
## Here the gem end. The test will come.
###############

$req = RequirementList.new(1,2,3, 4)

class MyTest < Test::Unit::TestCase
  #Following requirements exist, and must be tested sucessfull
  requirements $req

  def test_1()
    requirement(1)  #this test is testing requirement 1
    assert_equal(2,1+1)
  end
  def test_2()
    requirement(2)
    assert_equal(3,1+1)
  end
  def test_3()
    #no assignment to requirement 3
    pend 'pend'
  end
end


class MyTest_4 < Test::Unit::TestCase
  #Following requirements exist, and must be tested sucessfull
  requirements $req

  def test_4()
    requirement(4)  #this test is testing requirement 4
    assert_equal(2,1+1)
  end
end

результат:

Loaded suite testing_traceability_solutions
Started
.FP.

  1) Failure:
test_2(MyTest)
    [testing_traceability_solutions.rb:89:in `test_2'
     testing_traceability_solutions.rb:24:in `run_test']:
<3> expected but was
<2>.

  2) Pending: pend
test_3(MyTest)
testing_traceability_solutions.rb:92:in `test_3'
testing_traceability_solutions.rb:24:in `run_test'

Finished in 0.65625 seconds.

4 tests, 3 assertions, 1 failures, 0 errors, 1 pendings, 0 omissions, 0 notifications
50% passed
Requirements overview:
Requirement 1 was tested in MyTest#test_1
Requirement 2 was unsuccessfull tested in MyTest#test_2
Requirement 3 was not tested
Requirement 4 was tested in MyTest_4#test_4

Если вы думаете, что это может быть решением для вас, пожалуйста, дайте мне отзыв. Тогда я попытаюсь построить из него жемчужину.


Пример кода для использования с shoulda

#~ require 'test4requirements' ###does not exist/use code above
require 'shoulda'
#use another interface ##not implemented###
#~ $req = Requirement.new_from_file('requirments.txt')

class MyTest_shoulda < Test::Unit::TestCase
  #Following requirements exist, and must be tested sucessfull
  #~ requirements $req

  context 'req. of customer X' do
    #Add requirement as parameter of should
    # does not work yet
    should 'fullfill request 1', requirement: 1  do
      assert_equal(2,1+1)
    end
    #add requirement via requirement command
    #works already
    should 'fullfill request 1' do
      requirement(1)  #this test is testing requirement 1
      assert_equal(2,1+1)
    end
  end #context
end    #MyTest_shoulda
person knut    schedule 05.08.2011
comment
Это кажется хорошей идеей - хотя я думал о том, чтобы свернуть свой собственный по-другому. Мои запросы находятся в текстовом файле с тегом напротив каждого [REQnnnn]. Затем я добавляю этот тег в строку should в каждом тесте (я использую shoulda). Моим последним шагом будет написание класса TestRunner, который запускает модульные тесты, собирает выходные данные и сверяет их с txt-файлом требований. Спасибо за вдохновение. - person Stewart Johnson; 06.08.2011
comment
Я только что добавил пример кода для использования с shoulda. Вы думаете что-то подобное? Requirement#new_by_file будет читать текстовый файл с требованиями. У вас есть специальные форматы? Я бы порекомендовал yaml-файл. - person knut; 06.08.2011
comment
Предварительный выпуск этого кода в виде гема доступен по адресу rubygems.org/gems/test4requirements. - person knut; 07.08.2011
comment
На данный момент требования просто в текстовом файле, ничего особенного. YAML больше подходит для обработки. Спасибо за вашу работу - я сниму его через минуту! - person Stewart Johnson; 07.08.2011

С cucumber вы можете сделать так, чтобы ваше требование было тестом, более отслеживаемым не бывает :)

Таким образом, одно требование — это функция, а у функции есть сценарий, который вы хотите протестировать.

# addition.feature

Feature: Addition
  In order to avoid silly mistakes
  As a math idiot 
  I want to be told the sum of two numbers

  Scenario Outline: Add two numbers
    Given I have entered <input_1> into the calculator
    And I have entered <input_2> into the calculator
    When I press <button>
    Then the result should be <output> on the screen

  Examples:
    | input_1 | input_2 | button | output |
    | 20      | 30      | add    | 50     |
    | 2       | 5       | add    | 7      |
    | 0       | 40      | add    | 40     |

Затем у вас есть определения шагов, написанные на ruby, сопоставленные со сценарием.

# step_definitons/calculator_steps.rb

begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end 
require 'cucumber/formatter/unicode'
$:.unshift(File.dirname(__FILE__) + '/../../lib')
require 'calculator'

Before do
  @calc = Calculator.new
end

After do
end

Given /I have entered (\d+) into the calculator/ do |n|
  @calc.push n.to_i
end

When /I press (\w+)/ do |op|
  @result = @calc.send op
end

Then /the result should be (.*) on the screen/ do |result|
  @result.should == result.to_f
end
person kreek    schedule 05.08.2011
comment
Это выглядит красиво, но я не уверен, что это сработает для меня. Например, один из моих тестов проверяет возвращаемое значение метода, представляющего собой трехмерный массив, содержащий около 150 элементов. Как бы я сделал это в огурце? (Я уже написал это в test/unit.) - person Stewart Johnson; 06.08.2011