testcontainers / testcontainers-ruby

Testcontainers for Ruby
MIT License
120 stars 16 forks source link

DB Migrations #17

Closed kevinrobayna closed 1 year ago

kevinrobayna commented 1 year ago

Hello, Thank you so much for getting this together! I was trying it out but the specs currently fails because he missing migrations.

  config.before(:suite) do
    container = Testcontainers::PostgresContainer.new("postgres:15")
    config.postgres_container = container.start
    ENV["DATABASE_URL"] = config.postgres_container.database_url

    # fails here
    ActiveRecord::Migration.maintain_test_schema! 
  end

  config.after(:suite) do
    config.postgres_container&.stop
    config.postgres_container&.remove
  end

Do you have any ideas of wether we can provide to the testcontainer a schema file to start with or something to avoid running db migrations in the specs

guilleiguaran commented 1 year ago

@kevinrobayna Hi there, can you post the error backtrace here to investigate this in detail?

kevinrobayna commented 1 year ago

@kevinrobayna Hi there, can you post the error backtrace here to investigate this in detail?

oh sorry it just fails saying that the migrations aren't applied

PedroI920224 commented 1 year ago

@kevinrobayna I hope you're doing well, can you give us more context? currently, I'm running Postgres with the version postgres:15 and is working correctly:

image
container = Testcontainers::PostgresContainer.new("postgres:15")
client = PG.connect(host: 'localhost', user: "test", password: "test", port: 57567, dbname: "test")
client.exec("SELECT 1 AS number").first

the previous code was executed on Linux and Mac.

I think that you can give us more context if you run:

container.start
container.logs

and paste here the output

guilleiguaran commented 1 year ago

Hi @kevinrobayna, this configuration worked for me in Rails project with RSpec:

First, looks like ActiveRecord connections are loaded before the before(:suite) block, so we need to create the container before, for reference I put the configuration just before the ActiveRecord::Migration.maintain_test_schema! generated by RSpec installer by default:

RSpec.configuration.add_setting :postgres_container, default: nil
RSpec.configuration.postgres_container = Testcontainers::PostgresContainer.new("postgres:15").start
ENV['DATABASE_URL'] = RSpec.configuration.postgres_container.database_url(database: 'pgblog_test')

Rails.application.load_tasks
Rake::Task['db:test:prepare'].invoke # require 'rake' somewhere in the top of the file.
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  abort e.to_s.strip
end

And finally, stopped and removed the container in the after(:suite) block in the rails_helper.rb:

  config.after(:suite) do
    config.postgres_container&.stop
    config.postgres_container&.remove
  end

Also, IIRC, the config of database.yml have over DATABASE_URL so if you want to use the env variable instead of using establish_connection manually you can set the url: <%= ENV["DATABASE_URL"] %> key in database.yml.

As you can see, this requires extra setup (calling rake db:test:prepare in RSpec instead of invoking it before running the rspec command). I think we should add an RSpec integration to the gem to make all this easier, e.g simplify everything with just:

   require 'testcontainers/postgres/rspec'

Let me know if the suggestion works for your project.

kevinrobayna commented 1 year ago

Hi @kevinrobayna, this configuration worked for me in Rails project with RSpec:

First, looks like ActiveRecord connections are loaded before the before(:suite) block, so we need to create the container before, for reference I put the configuration just before the ActiveRecord::Migration.maintain_test_schema! generated by RSpec installer by default:

RSpec.configuration.add_setting :postgres_container, default: nil
RSpec.configuration.postgres_container = Testcontainers::PostgresContainer.new("postgres:15").start
ENV['DATABASE_URL'] = RSpec.configuration.postgres_container.database_url(database: 'pgblog_test')

Rails.application.load_tasks
Rake::Task['db:test:prepare'].invoke # require 'rake' somewhere in the top of the file.
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  abort e.to_s.strip
end

And finally, stopped and removed the container in the after(:suite) block in the rails_helper.rb:

  config.after(:suite) do
    config.postgres_container&.stop
    config.postgres_container&.remove
  end

Also, IIRC, the config of database.yml have over DATABASE_URL so if you want to use the env variable instead of using establish_connection manually you can set the url: <%= ENV["DATABASE_URL"] %> key in database.yml.

As you can see, this requires extra setup (calling rake db:test:prepare in RSpec instead of invoking it before running the rspec command). I think we should add an RSpec integration to the gem to make all this easier, e.g simplify everything with just:

 require 'testcontainers/postgres/rspec'

Let me know if the suggestion works for your project.

Thank you for looking into this.

TIL, the connection happens even the before 😅

will try that on a repository at work and let you know of the findings!