ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: …

Problem

You have a Ruby on Rails application/engine with some tests in Minitest and using postgresql as the db. In the original development environment all the tests that use fixtures and foreign key validations pass, but when you try to move to a different environment with new database some or all of you tests fail with a message that refers to the ForeignKeyViolation as in:

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:  insert or update on table "tbl_name" violates foreign key constraint "tbl_name_cln_name_fk_tbl_name_fk"
DETAIL:  Key (col_name)=(VALUE) is not present in table "fk_tbl_name".

Solution

Since active record disables the foreign keys before loading the fixtures in the database, the user that you run the tests with needs to have superuser permissions in the test database. Otherwise the fixtures are not loaded. So add the superuser permissions to your user:

psql -U super_user
alter role use_for_tests superuser;

You may need to drop and create your test database.

Capturing output with UnitTest in Ruby

Problem

You have some ruby code, a rake task for example, that outputs some results in the standard output, but you would like to test it in your unit tests.
MiniTest has capture_io and capture_subprocess_io, but there is nothing similar in UnitTest.

Solution

Looking at the code of the above MiniTest assertions you can create your own assertion to be used in your tests.

Create a new file under your test folder called MyAssertions and use the following code (taken from MiniTest), for your capture_output assertion:

module MyAssertions
  module CaptureOutput
    # Use as:
    # out = capture_output { method_to_call(param1, param2) }
    # output = out[0], error = out[1] or
    # output = out.first, error = out.last
    def capture_output
      require 'stringio'

      captured_stdout, captured_stderr = StringIO.new, StringIO.new
      orig_stdout, orig_stderr = $stdout, $stderr
      $stdout, $stderr         =  captured_stdout, captured_stderr

      begin
        yield
      ensure
        $stdout = orig_stdout
        $stderr = orig_stderr
      end
      return captured_stdout.string, captured_stderr.string
    end

    def capture_subprocess_output
      require 'tempfile'

      captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
      orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
      $stdout.reopen captured_stdout
      $stderr.reopen captured_stderr

      begin
        yield

        $stdout.rewind
        $stderr.rewind

        [captured_stdout.read, captured_stderr.read]
      ensure
        captured_stdout.unlink
        captured_stderr.unlink
        $stdout.reopen orig_stdout
        $stdout.reopen orig_stderr
      end
    end
  end

  include CaptureOutput
end 

Then include it in your test_helper.rb file with:

require './test/my_assertions.rb'

and use it in your tests like:

out = capture_output { method_called(prm1, prm2) }

assert_match(/required_value/, out.first)

minitest assert_routing with method included in path

Problem

When trying to use the minitest assert_routing with the first parameter representing the path as a hash that includes both the path and the method, and run the tests rails complains about SyntaxErrors.

When trying to use it as suggested in the ‘Rails 4 Test Prescriptions’ Pragmatic Programmers book (p. 172 – Minitest and Routing) which is:

assert_routing({ path: "/projects", method: "post" }, 
controller: "projects", action: "create")

the error is:

SyntaxError: 
/.../test/controllers/projects_controller_test.rb:12: 
syntax error, unexpected ',', expecting ')'
... '/projects', method: 'post' },  controller: 'projects', act...

even when trying to have the second parameter as a hash:

assert_routing({ path: "/projects", method: "post" }, 
{ controller: "projects", action: "create" })

the error is similar:

SyntaxError: 
/.../test/controllers/projects_controller_test.rb:12:
 syntax error, unexpected ',', expecting ')'
... '/projects', method: 'post' }, 
{ controller: 'projects', ac...

Solution

Seems that you need to pass the parameters enclosed in brackets, so the following would work:

assert_routing  ({ path: '/projects', method: 'post' }), 
({ controller: 'projects', action: 'create' })