Fluentdのプラグイン開発について公式ドキュメントが充実してて良いのですが
RSpecの例は載って無さそうなので、いろいろ試行錯誤した内容を書きます。
プロジェクトスケルトン作成
$ fluent-plugin-generate filter leef
RSpecを利用するための準備
$ rspec --init
.rspec
ファイル、spec
ディレクトリ、spec/spec_helper.rb
の用意。
*.gemspec
spec.add_development_dependency "test-unit"
spec.add_development_dependency "rspec"
spec.add_development_dependency "rspec_junit_formatter"
spec.add_development_dependency "simplecov"
spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
test-unit
は、テストドライバの部分で使われるので記述が必須。
rspec_junit_formatter
は、CircleCIを使う際には必要となる。
Rakefile
スケルトンでは、test-unit
用になっているのでRSpec
用に以下の通りに書き換える。
# -*- coding: utf-8 -*-
# frozen_string_literal: true
require "bundler"
require "rspec/core/rake_task"
Bundler::GemHelper.install_tasks
RSpec::Core::RakeTask.new("spec")
task: default => :spec
spec/spec_helper.rb
# coverage計測用に、simplecovを読み込む。
require "simplecov"
# テスト用に`fluent/test`を読み込む。
require "fluent/test"
# `RSpec`実行時に`Test::Unit`の自動実行結果が表示されてしまうので、自動実行されないようにする。
Test::Unit::AutoRunner.need_auto_run = false if defined? Test::Unit::AutoRunner
# テスト用のヘルパーを読み込む
require "fluent/test/helpers"
# フィルタプラグイン用のテストドライバを読み込む(フィルタプラグイン作成時)
require "fluent/test/driver/filter"
# 開発するプラグインを読み込む
require "fluent/plugin/filter_leef"
RSpec.configure do |config|
# Fluent::Test.setupを呼び出しテスト用の初期化処理を行う
config.before(:all) { Fluent::Test.setup }
以下略
.simplecov
RSpec
実行時にsimplecov
を動かす際の除外設定用。
# -*- coding: utf-8 -*-
# frozen_string_literal: true
SimpleCov.start do
add_filter "/vendor/bundle/"
end
.rspec
--require spec_helper
--format documentation
本体コードの記述(例)
lib/flunet/plugin/filter_leef.rb
# -*- coding: utf-8 -*-
# frozen_string_literal: true
require "fluent/plugin/filter"
module Fluent
module Plugin
class FilterLEEF < Fluent::Plugin::Filter
Fluent::Plugin.register_filter("leef", self)
config_param :key_name1, :string
config_param :key_name2, :string
def configure(conf)
super
end
def filter(tag, time, record)
省略
return filtered_record
end
def hello
"hello"
end
end
end
end
specの記述
spec/fluent/plugin/filter_leef_spec.rb
# -*- coding: utf-8 -*-
# frozen_string_literal: true
# RSpec.describeには、本体コードに書いたクラスを指定する
RSpec.describe Fluent::Plugin::FilterLEEF do
include Fluent::Test::Helpers
# フィルタ用のテストドライバの生成
let(:driver) do
Fluent::Test::Driver::Filter.new(described_class).configure(conf)
end
# ドライバのインスタンスを取り出す
# 本体コードのクラスのインスタンスメソッドは、`filter.hogehoge(foobar)`で呼び出せる
let(:filter) { driver.instance }
# fluent.confの内容のフィルタに関する部分が`configure`メソッドを通して
# フィルタプラグインに渡ってくる
# その内容をconfとして用意する
let(:conf) do
%(
key_name1 message1
key_name2 message2
)
end
# describeには、メソッドと引数を記述する
# インスタンスメソッドの場合は、#を前置
describe "#filter(tag, time, record)" do
# filterメソッドのテストをする場合は、テストドライバを経由して
# フィルタプラグインの動作を確認することになるので
# driver.runおよびdriver.feed経由でfilterメソッドを呼び出し、
# dirver.filtered_recordsメソッド呼び出しの結果に含まれる
# 値をチェックする
subject { driver.filtered_records.first }
let(:record) { {"message" => "hogehoge"} }
before do
driver.run(default_tag: "test") { driver.feed(Time.now.to_i, record) }
end
it { is_expected.to eq "fugafuga" }
end
describe "#hello" do
# filterメソッド以外のインスタンスメソッドのテストをしたい場合は
# テストドライバから取り出したインスタンス(この場合はfilterインスタンス)の
# インスタンスメソッド(この場合はhelloメソッド)を呼んでテストする
subject { filter.hello }
it { is_expected.to eq "hello" }
end
end
fluent.conf
に以下の記述がある場合。
<filter foo.*>
@type leef
key_name1 message1
key_name2 message2
</filter>
conf
として以下の内容が渡ってくる。
"key_name1 message1\nkey_name2 message2"
テストコード上のconf
も同様に文字列を用意すればよいが
%記法を使うと記述が少し書きやすくなる。
let(:conf) do
%(
key_name1 message1
key_name2 message2
)
end
fluentdのログ出力内容を確認したい場合
本体コード内でlog.error "hogehoge"
など、
fluentdのログに出力する箇所があり、
ログ出力内容の確認を行いたい場合は次のようにする。
describe "#sample" do
subject { dirver.logs }
before do
filter.sample
end
it { is_expected.to include(include("hogehoge")) }
end
RSpec実行方法
プロジェクトルートで。
bundlerを使って関連パッケージの用意。
$ bundle install --path vendor/bundle
bundler経由でrspecを実行
$ bundle exec rspec