【rails5.2 + mysql + rspec】既存のDB上でrspecを実行したらTable doesnt existと言われた。
以前、docker上にDB立ててリモートからdumpしてrestoreするの時間かかるからそのままリモートDBに繋げるようにすれば楽だよねーって記事を書きました。
僕が今回業務で対応していたのが、DBからデータを取得するだけのアプリケーションだったのでこれで良いと思っていました。
そう、思っていたのです。。。。
だってselectだけだから間違えてDB吹っ飛ぶなんてことは絶対ないと思っていたから・・・
しかし今回、DB吹っ飛びましたw
開発用DBなので別に影響範囲もくそもないのですが、精神的に穏やかでなかったのでログとして残します。
構成
今回はこんな感じ。
もともとPHPで書かれたアプリケーションとそのDBとしてmysqlを利用していました。
今回僕は、phpのアプリケーションのある動作をフックにし、そのmysqlのDBともう一つ違うところにあるDBにアクセスし、データ統合やら整形をして、違うDBにpostするというアプリケーションをrailsで対応しようとしていました。
実際
僕のやることとしては、
「新しいレポジトリ作成して、既存のDBたちにアクセスできるように0からのアプリケーションを作る」
です。
なのでロジックやらなんやら書いてテスト書いてdeployフロー作って、deployさせるってかんじというのはすぐ思いつくと思います。
なので実行しました。
そしてテストを書きます。
Gemfileにrspec系のgemを加えます。
group :development, :test do ・ ・ ・ gem 'rspec-rails' gem 'rails-controller-testing' end
そしてターミナル上でbundle install。僕はdocker使っていたのでこんなかんじです。
$ docker-compose run web bundle install
そしてrspecで必要なファイルを用意する必要があるのでrailsのコマンドを打ちます。
$ docker-compose run web bundle exec rails generate rspec:install
これで必要なものが完成。
で、テスト書いてrspecを実行。
$ docker-compose run web bundle exec rspec spec/
そしたらTable doesnt existとエラーになりました。
この後は何回やってもこのエラーになってしまいます。
なぜか??
rspecで必要なファイルを用意した時に使ったrailsのコマンド
$ docker-compose run web bundle exec rails generate rspec:install
を実行した時にrails_helper.rbというファイルが作られます。
その中のコードを見ていくと
# Checks for pending migrations before tests are run. # If you are not using ActiveRecord, you can remove this line. ActiveRecord::Migration.maintain_test_schema!
というのがあります。
結論、この設定のせいでDBが吹っ飛びました。
これrails4.1から設定されているものらしいです。
github.com
コードを追う
githubでいうとここですね。
maintain_test_schemaというのは、テスト実行時にActive Recordがテスト用データベーススキーマをdb/schema.rb(またはdb/structure.sql)に基いて最新の状態にするかどうかを指定するものらしいです。
で、続きでここですね。
でこのif文を見てみます。
if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
今回はこれですね。
def any_migrations? migrations.any? end
結論
僕はphpを触ったことがないのでなんとも言えないのですが、今回はphpでDBがもともと作成されていました。
つまり僕が新しく作ったrailsのアプリケーションにはmigrationのファイルが一つもないわけです。
そして
current_config = Base.connection_config Base.clear_all_connections! system("bin/rails db:test:prepare") # Establish a new connection, the old database may be gone (db:test:prepare uses purge) Base.establish_connection(current_config)
このロジックが流れ、新しいデータベースを作りにいってしまいました。
結果、何もない綺麗さっぱりなDBが出来てしまった→DBが吹っ飛んだ
ということになっていたらしいです。
コメントアウトでも書いてあるのですが、自然と作られるものってそこまで読まないので気をつけたいものです。。
というか、やっぱり何かあったときのためにdumpしようねって話ですね。
おまけ(docker内へのdumpの仕方)
ターミナルで
$ mysqldump -u USER_NAME -p -h HOST_NAME DB_NAME > ~/Desktop/hoge.sql OUTPUT_FILE_NAME:これはtest.sql的なので空ファイルを自分で作っておくということです。
でdump出来ます。
もし踏み台使わないと行けない場合は、トンネル掘ってから実行すれば問題ないです。
でdockerでdb作るときにvolume忘れずにセットしする。
docker-compose.ymlの中で忘れずに。 volumes: - ./db/mysql/volumes:/var/lib/mysql
docker起動してからdumpしたファイルをコピーし、docker内に入ってrestoreする。
$ docker cp ~/Desktop/hoge.sql コンテナの名前:/var/lib/mysql $ docker exec -it コンテナの名前 /bin/bash # mysql -u root -p 適当なdatabase名 < /var/lib/mysql
終わり。