rspec测试比rails控制台慢得多

时间:2015-11-10 21:36:56

标签: ruby-on-rails ruby rspec factory-bot

我一直在花费一整天的时间来弄清楚为什么我的rspec-rails测试套件需要很长时间才能完成(这并不总是这样)我使用了--profile来确定哪些测试是花了很长时间,似乎每个测试点击数据库每次查询需要30秒。

采用以下示例行:

MyModel.create(args)

如果我在rails控制台中运行此行,它会立即完成,但如果我将其包含在rspec测试中,它会为该测试的完成时间增加30秒。此外,这一行只是一个例子,如果我使用Factory girl或使用<<创建关系同样的30秒税似乎也适用。

当然必须配置错误的东西。

见下我的spec_helper.rb文件:

require 'simplecov'
require 'metric_fu/metrics/rcov/simplecov_formatter'
SimpleCov.formatter = SimpleCov::Formatter::MetricFu
SimpleCov.start 'rails'

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'database_cleaner'
require 'carrierwave'

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.use_transactional_fixtures = true
  config.infer_base_class_for_anonymous_controllers = false
  config.order = "random"
  config.mock_with :rspec
  config.include JsonSpec::Helpers
  config.include FactoryGirl::Syntax::Methods
  config.include Devise::TestHelpers, type: :controller

  # config.raise_errors_for_deprecations!
  config.infer_spec_type_from_file_location!

  config.expect_with :rspec do |c|
    c.syntax = [:should, :expect]
  end

  config.before(:suite) do
    begin
      EphemeralResponse.activate
      DatabaseCleaner.strategy = :truncation
      DatabaseCleaner.clean_with(:truncation)
      DatabaseCleaner.start
      # FactoryGirl.lint
    ensure
      DatabaseCleaner.clean
    end
  end

  config.before(:each) do
    Bullet.start_request if Bullet.enable?
  end

  config.after(:each) do
    if Bullet.enable? #&& Bullet.notification?
      # Bullet.perform_out_of_channel_notifications
      # Bullet.end_request
    end
    DatabaseCleaner.clean
  end

  config.after(:suite) do
    EphemeralResponse.deactivate
  end
end

我对rspec并不是很了解,并且我没有写出这个测试套件的很大一部分,所以也许这对某些人来说真的很明显,但我整天都在反对它。

编辑:我要添加一个模型测试:这个测试需要5分钟...

require 'spec_helper'
describe CourseAssignment do

    let(:instructor) { create(:instructor) }
    let(:student) { create(:student, id: 2) }
    let(:bundle) { create(:bundle) }
    let(:course) { create(:course) }
    let(:assignment) { create(:course_assignment, course: course) }

    it { should belong_to(:course) }
    it { should belong_to(:user) }

    describe 'CourseAssignment deletion' do
        it 'should not delete the Course or the Student' do
      instructor.courses << course # 30 seconds
      instructor.courses.last.students << student # 30 seconds
      CourseAssignment.create(course_id: course.id, user_id: student.id) # 30 seconds
      expect{CourseAssignment.last.destroy}.to change(CourseAssignment, :count).by(-1)
      expect(Student.count).to eq(1)
      expect(Course.count).to eq(1)
      expect(instructor.courses).to include(course)
        end
    end

    describe 'Student deletion' do
        it 'should delete the CourseAssignment & Student, but not the  Course' do
      instructor.courses << course # 30 seconds
      instructor.courses.last.students << student # 30 seconds
      CourseAssignment.create(course_id: course.id, user_id: student.id) # 30 seconds
      expect(student.course_assignments.count).to eq(2)

      expect{student.destroy}.to change(CourseAssignment, :count).by(-2)
      expect(Course.last.students).to be_empty
      expect(Course.count).to eq(1)
      expect(instructor.courses).to include(course)
        end
    end

    describe 'Course deletion' do
        it 'should delete the CourseAssignment' do
      instructor.courses << course # 30 seconds
      instructor.courses.last.students << student # 30 seconds
      CourseAssignment.create(course_id: course.id, user_id: student.id)

      expect{course.destroy}.to change(Course, :count).by(-1)
      expect(CourseAssignment.count).to be(0)
      instructor.reload
      expect(instructor.courses).to be_empty
        end
    end

    it 'should require a course_id' do
        no_course_id_assignment = CourseAssignment.create(course_id: '', user_id: 2) # 30 seconds
        no_course_id_assignment.should_not be_valid
    end

    it 'should require a user_id' do
        no_student_id_assignment = CourseAssignment.create(course_id: 2, user_id: '') # 30 seconds
        no_student_id_assignment.should_not be_valid
    end

end

编辑2:我发现这与我的电脑有关(Mac os x 10.10.5)。当我在circle ci上运行我们的测试套件时,整个套件在不到5分钟的时间内运行。

编辑3:继承我的database.yml

# MySQL.  Versions 4.1 and 5.0 are recommended.
#
# Install the MySQL driver:
#   gem install mysql2
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
development:
  adapter: mysql2
  encoding: utf8
  database: xxxx
  username: xxxx
  password: xxxx
  host: localhost

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: mysql2
  encoding: utf8
  database: xxxx_test
  username: xxxx
  password: xxxx
  host: localhost

production:
  adapter: mysql2
  encoding: utf8
  host: localhost
  database: xxxx
  username: xxxx
  password: xxxx
  port: 3306

编辑4:刚才发现改变我的wifi网络可以解决这个问题。问题似乎与第一个无法访问mixpanel.com的网络相关的中断有关。

1 个答案:

答案 0 :(得分:2)

执行rails c时,请注意加载需要一些时间,因为它正在启动数据库并加载Rails类,这些类将立即可用&#34;即时#34;。

每个测试规范每次都会重新创建一个干净的DB平板,因此它应该比在控制台中运行相同的命令花费更多的时间(因为已经设置了DB)。您可以通过正确使用before(:all)并以尽可能多地使用相同实例的方式设置测试来缓解这种情况。