So here's my problem:
I am writing unit tests for my Rails models and I have a whole set of examples that each require the same setup in order to run. If I'm not mistaken, the usual way to set things up the same way for multiple RSpec tests is to use a before(:each) block, like this:
describe Model do
before(:each) do
# Complex setup
end
# Examples
end
Unfortunately the set of examples which needs this setup is starting to get rather large, and completing this complex setup proceedure for each and every test takes a long time. I tried doing this:
describe Model do
before(:all) do
# Complex setup
end
# Examples
end
But this method doesn't roll back my setup though after I'm done with it, which causes problems in later tests. What I really want is to do something like this:
describe Model do
around(:all) do |examples|
transaction do
# Complex setup
examples.run
raise ActiveRecord::Rollback
end
end
# Examples
end
RSpec doesn't currently support an around(:all) hook however. Any ideas?
The easiest way to do this would just be to use an after(:all) block to clean up after your tests.
Related
I use before(:all) in my tests for database initialization, which causes no problems for my test cases... except one. I'd like to run this special case before or after others, as I want to initialize database for it separately. Is there some way to do this? I'd like to avoid replacing before(:all) with before(:each), because I'd like to keep my tests fast. Is it possible?
before(:all) except one - is it possible?
Probally not. before(:all) runs before all the examples in that scope and there is no way to hoist examples above it.
I think a better idea is to work around the problem and just create different contexts:
RSpec.describe Thing do
context "with :all" do
before(:all) do
# ...
end
# ...
end
context "without :all" do
# ...
end
end
Use shared contexts or the outer scope if you need to share setup steps or variables. Use shared examples if you want to run the same examples in different contexts.
I'd like to run this special case before or after others, as I want to
initialize database for it separately. Is there some way to do this?
You can use the --order defined option when running rspec to run the tests sequentially. However this sets you up for test ordering issues than can mask critical bugs in the application. Fast test are worthless if they don't catch bugs.
You can also use :order => :defined metadata to set the order per context. And for the reason in the beginning of this question this is a fools errand.
Like a lot of non-greenfeild projects, I arrived at my new job to find that most tests are performed using feature specs and not controller/model specs. Naturally this means that the tests take a really long time to execute.
Is there a way I can setup RSpec so that my feature tests run last - and they will only run if all other tests first succeed. I am migrating the application over to controller tests to speed execution, but there is no need to wait the 15mins for the feature tests to complete if there is an issue with model validation or one of my controllers is busted.
Is what I want to do possible?
(I am running the tests in RubyMine at the moment; but I can move to rake spec if need be).
Although I haven't actually tried this myself, reading through the documentation it appears it is possible to do what you wish.
Add the following to your spec_helper.rb to override the global ordering and force rspec to fail on first failure.
RSpec.configure do |config|
config.register_ordering :global do |examples|
feature, other = examples.partition do |example|
example.metadata[:type] == :feature
end
other + feature
end
config.fail_fast = true
end
I was wondering if there is a script that can take existing codebase and generate unit tests for each method in controllers. By default all would be passing since they would be empty and i can remove tests i for methods i dont feel important.
This would save huge time and increase testing. Since i'd have to define only what each method should output and not boilerplate that needs to be written.
You really shouldn't be doing this. Creating pointless tests is technical debt that you don't want. Take some time, go through each controller and write a test (or preferably a few) for each method. You'll thank yourself in the long run.
You can then also use test coverage tools to see which bits still need testing.
You can use shared tests to avoid repetition. So for example with rspec, you could add the following to your spec_helper/rails_helper
def should_be_ok(action)
it "should respond with ok" do
get action.to_sym
expect(response).to be_success
end
end
Then in your controller_spec
describe UserController do
should_be_ok(:index)
should_be_ok(:new)
end
I have been looking around the internet for a long, frustrating, while, and I'm still quite confused as to what the purpose of the teardown() method is in MiniTest and how it should be used.
The basic gist I have is that it is 1-run after each test, and 2-undoes things that were done during the test in order to allow future tests to run in a clean environment.
However, I am unclear on the nature of things it needs to undo: Does it need to empty the DB? Reset class variables? etc.
I am also unclear on whether the method is supposed to be explicitly filled out or not. I have found many examples where teardown() is completely left out of the example.
(My best guess is that there is a super-method teardown that runs automatically and takes care of certain things. That would explain why it is often left out, and would also explain why some things are reset in a given teardown() method and some aren't. But I still don't know which things are and which aren't.)
In short:
Does teardown need to be explicitly created? In what circumstances would it need to be overwritten and in which wouldn't it be?
The simplest answer is that you use #teardown in every test but you don't need to worry about it. Similar to the Rails lifecycle, there is a Minitest lifecycle. There are hooks to inject logic and behavior to be used by your tests. The main one in Rails tests is the database transaction. Each test that uses ActiveSupport::TestCase runs in a database transaction. The flow is like this:
Create the database transaction (Minitest::Test#setup)
Run your test method (MyTest#test_something)
Rollback the database transaction (Minitest::Test#teardown)
It is somewhat common for folks to use #setup to create objects for use in tests. After the test method completes the test object is garbage collected, so most folks don't use #teardown to clean up after the test. Because of this #teardown is typically a more advanced feature that you don't normally use when writing tests. I see it used much more often in testing libraries that enhance Minitest.
But there are times I do use #teardown in my tests. Here is an example of when I might use it.
require "minitest/autorun"
class Foo
def initialize namer
#namer = namer
end
def name
#namer.name
end
end
class FooTest < Minitest::Test
def setup
#namer_mock = Minitest::Mock.new
#namer_mock.expect :name, "foo"
#foo = Foo.new #namer_mock
end
def test_name
assert_equal "foo", #foo.name
end
def teardown
#namer_mock.verify
end
end
I'd like to init the data base once everytime i run tests, rather than every test.
I know with Rspec there is before(:all), but I haven't been able to get that working. I was wondering if rails had something similar.
Firstly: there used to be a before(:all) equivalent in Test::Unit but it was removed (don't know why).
Secondly: there are very good reasons not to do what you are trying to do - tests are meant to be run independently of one another, not rely on state that's in the db. This way you can guarantee that it's testing exactly what you are expecting it to test.
If you have one test that changes the state of the db, and you move it and it runs after another test which expects it to be another state - you run into problems. Thus, all test must be independent.
Thus: the db is rolled back to its pristine state and re-seeded every time.
If you really want some state that the db is always in - then set it up in the fixtures... and just realise that the db will be re-loaded for each test.
If you are having trouble with load-times... then consider figuring out some other way around the problem - eg don't use huge numbers of fixtures, instead use Factories to only create the data that you need for each individual test.
If there's some other reason... let us know - we may have a solution for it.
Edit: if you really need it, I actually wrote a monkey patch for this a long while back:
"faking startup and shutdown"
All things to run before everything just go in the top of the class
require 'test_helper'
class ObjectTest < ActiveSupport::TestCase
call_rake("db:bootstrap RAILS_ENV=test")
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
# make sure our user and account got created
puts "||||||||||||||||||||||||||||||||||||||||||||||"
puts "| propsal_test.rb"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "||||||||||||||||||||||||||||||||||||||||||||||"