Coverage 모듈

Coverage 모듈

Coverage는 Ruby의 커버리지 측정 기능을 제공해요. 이 기능은 실험적이라 앞으로 API가 바뀔 수 있어요. 지켜야 할 제약 하나가 있는데, 현재는 프로세스 전역 커버리지 측정만 지원해요. 스레드 단위 커버리지는 측정할 수 없어요.

출처: Ruby 4.0 API

본문

Coverage는 Ruby의 커버리지 측정 기능을 제공해요. 실험적 기능이라 API는 앞으로 바뀔 수 있어요.

주의: 현재는 프로세스 전역 커버리지 측정만 지원해요. 스레드 단위 커버리지는 측정할 수 없어요.

사용법

  • require "coverage"
  • Coverage.start 실행
  • Ruby 소스 파일 require 또는 load
  • Coverage.result 는 파일명을 키로, 커버리지 배열을 값으로 하는 해시를 돌려줘요. 커버리지 배열은 줄마다 그 줄이 인터프리터에 의해 실행된 횟수를 담아요. nil 값은 그 줄(예: elseend 같은 줄)에 대해 커버리지가 비활성화됐다는 뜻이에요.

예시

[foo.rb]
s = 0
10.times do |x|
  s += x
end

if s == 45
  p :ok
else
  p :ng
end
[EOF]

require "coverage"
Coverage.start
require "foo.rb"
p Coverage.result  #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}

Lines 커버리지

커버리지 시작 시 모드를 명시하지 않으면 lines 커버리지가 실행돼요. 각 줄이 실행된 횟수를 보고해요.

require "coverage"
Coverage.start(lines: true)
require "foo.rb"
p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}}

lines 커버리지 결과의 값은 각 줄이 실행된 횟수를 담은 배열이에요. 이 배열에서 순서가 중요해요. 예를 들어 index 0의 첫 항목은 커버리지 측정 동안 이 파일의 1번째 줄이 실행된 횟수(이 예시에서는 한 번)를 보고해요.

nil 값은 그 줄(예: elseend)에 대해 커버리지가 비활성화됐다는 뜻이에요.

Oneshot Lines 커버리지

Oneshot lines 커버리지는 커버리지가 실행되는 동안 실행된 줄을 추적해서 보고해요. 몇 번 실행됐는지는 보고하지 않고, 실행됐는지 여부만 보고해요.

require "coverage"
Coverage.start(oneshot_lines: true)
require "foo.rb"
p Coverage.result #=> {"foo.rb"=>{:oneshot_lines=>[1, 2, 3, 6, 7]}}

oneshot lines 커버리지 결과의 값은 실행된 줄 번호를 담은 배열이에요.

Branches 커버리지

Branches 커버리지는 각 조건문 안의 각 분기가 몇 번 실행됐는지 보고해요.

require "coverage"
Coverage.start(branches: true)
require "foo.rb"
p Coverage.result #=> {"foo.rb"=>{:branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}}}

branches 해시의 각 항목은 조건문 하나예요. 그 값은 또 다른 해시로, 각 항목이 그 조건문 안의 분기를 나타내요. 값은 메서드가 실행된 횟수이고, 키는 분기를 식별하는 정보예요.

각 분기나 조건문을 식별하는 키를 이루는 정보는 왼쪽부터 다음과 같아요:

  • 분기 또는 조건문의 타입 라벨.
  • 고유 식별자.
  • 파일에서 나타나는 시작 줄 번호.
  • 파일에서 나타나는 시작 컬럼 번호.
  • 파일에서 나타나는 끝 줄 번호.
  • 파일에서 나타나는 끝 컬럼 번호.

Methods 커버리지

Methods 커버리지는 각 메서드가 실행된 횟수를 보고해요.

[foo_method.rb]
class Greeter
  def greet
    "welcome!"
  end
end

def hello
  "Hi"
end

hello()
Greeter.new.greet()
[EOF]

require "coverage"
Coverage.start(methods: true)
require "foo_method.rb"
p Coverage.result #=> {"foo_method.rb"=>{:methods=>{[Object, :hello, 7, 0, 9, 3]=>1, [Greeter, :greet, 2, 2, 4, 5]=>1}}}

methods 해시의 각 항목은 메서드 하나를 나타내요. 값은 메서드가 실행된 횟수이고, 키는 메서드를 식별하는 정보예요.

메서드를 식별하는 키를 이루는 정보는 왼쪽부터 다음과 같아요:

  • 클래스.
  • 메서드 이름.
  • 파일에서 메서드가 나타나는 시작 줄 번호.
  • 시작 컬럼 번호.
  • 끝 줄 번호.
  • 끝 컬럼 번호.

모든 커버리지 모드

이 단축키로 모든 커버리지 모드를 동시에 실행할 수도 있어요. 모든 커버리지 모드를 실행한다고 lines와 oneshot lines를 둘 다 실행하는 건 아니에요. 그 둘은 동시에 실행할 수 없거든요. 이 경우 lines 커버리지가 실행되는데, 여전히 줄이 실행됐는지를 알아내는 데 쓸 수 있기 때문이에요.

require "coverage"
Coverage.start(:all)
require "foo.rb"
p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil], :branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}, :methods=>{}}}

Class Methods

line_stub(file) → array

주어진 소스 코드에서 line 커버리지의 "스텁(stub)"을 만드는 간단한 헬퍼 함수예요.

# File ext/coverage/lib/coverage.rb, line 9
def self.line_stub(file)
  lines = File.foreach(file).map { nil }
  iseqs = [RubyVM::InstructionSequence.compile_file(file, coverage_enabled: false)]
  until iseqs.empty?
    iseq = iseqs.pop
    iseq.trace_points.each {|n, type| lines[n - 1] = 0 if type == :line }
    iseq.each_child {|child| iseqs << child }
  end
  lines
end

peek_result → hash

파일명을 키로, 커버리지 배열을 값으로 하는 해시를 돌려줘요. Coverage.result(stop: false, clear: false)와 같아요.

{
  "file.rb" => [1, 2, nil],
  ...
}

result(stop: true, clear: true) → hash

파일명을 키로, 커버리지 배열을 값으로 하는 해시를 돌려줘요. cleartrue면 카운터를 0으로 지워요. stoptrue면 커버리지 측정을 비활성화해요.

resume → nil

커버리지 측정을 시작하거나 재개해요.

주의: 현재는 프로세스 전역 커버리지 측정만 지원해요. 스레드 단위 커버리지는 측정할 수 없어요. 프로세스에 여러 스레드가 있다면 Coverage.resume/suspend로 제한된 코드 블록에서만 실행되는 커버리지를 포착하려 할 때, 잘못된 결과가 나올 수 있어요.

running? → bool

커버리지 통계가 현재 수집 중이면(Coverage.start 호출 후 Coverage.result 호출 전) true를 돌려줘요.

setup → nil

setup(:all) → nil

setup(lines: bool, branches: bool, methods: bool, eval: bool) → nil

setup(oneshot_lines: true) → nil

커버리지 측정을 설정해요.

이 메서드는 측정 자체를 시작하지 않는다는 점에 주의하세요. 측정을 시작하려면 Coverage.resume을 사용해요.

Coverage.start로 설정과 시작을 함께 할 수도 있어요.

start → nil

start(:all) → nil

start(lines: bool, branches: bool, methods: bool, eval: bool) → nil

start(oneshot_lines: true) → nil

커버리지 측정을 활성화해요. 자세한 내용은 Coverage 클래스 문서를 보세요. Coverage.setupCoverage.resume 을 합친 것과 같아요.

state → :idle, :suspended, :running

커버리지 측정의 상태를 돌려줘요.

supported?(mode) → true or false

주어진 모드에 대해 커버리지 측정이 지원되면 true를 돌려줘요.

모드는 다음 심볼 중 하나여야 해요: :lines, :oneshot_lines, :branches, :methods, :eval.

예시:

Coverage.supported?(:lines)  #=> true
Coverage.supported?(:all)    #=> false

suspend → nil

커버리지 측정을 일시 중단해요. Coverage.resume으로 측정을 다시 시작할 수 있어요.