I just, manually, discovered a migration error. I added a new field to a model, and forgot to add it into the model_params method of the controller. As a result the new field wasn't persisted to the database.
Easy enough to fix once I noticed the problem, but it got me to wondering if there was a way to detect this in testing. I would imagine something like a gem that would parse the schema and generate a set of tests to ensure that all of the fields could be written and that the same data could be read back.
Is this something that can be (or is) done? So far, my searches have led me to lots of interesting reading, but not to a gem like this...
It is possible to write what you would want. Iterate through all the fields in the model, generate params that mirrors those fields, and then run functional tests on your controllers. The problem is that the test is brittle. What if you don't actually want all the fields to be writable through params? What if you reference a model in another controller outside of the standard pattern? How will you handle generating data that would pass different validations? You would either have to be sure that your application would only be written in a certain way or this test would become more and more complex to handle additional edge cases.
I think the solution in testing would be to try to keep things simple; realize that you've made a change to the system and as a result of that change, corresponding tests would need to be updated. In this case, you would update the functional and unit tests affected by that model. If you were strictly adhering to Test Driven Design, you would actually update the tests first to produce a failing test and then implement the change. As a result, hopefully the updated functional test would have failed in this case.
Outside of testing, you may want to look into a linter. In essence, you're asking if you can catch an error where the parameters passed to an object's method doesn't match the signature. This is more catchable when parsing the code completely (i.e. compilation in a static type environment).
EDIT - I skipped a step on the linting, as you would also have to write your code a certain way that a linter would catch it, such as being more explicit of the method and parameters passed to it.
You might want to consider that such a gem may not exist because its not that practical or useful in real life.
Getting the columns off a model is pretty simple from the reflection methods that Active Record gives you. And yeah you could use that theoretically to automagically run a bunch of tests in loop.
But in reality its just not going to cut it. In real life you don't want every column to be assignable. Thats why you are using mass assignment protection in the first place.
And add to that the complexity of the different kinds of constraints and data types your models have. You'll end up with something extremely complex with just adds a bunch of tests with limited value.
If you find yourself omitting a property from mass assignment protection than your should try to cover that part of your controller either with a functional test or an integration test.
class ArticlesControllerTest < ActionController::TestCase
def valid_attributes
{
title: 'How to test like a Rockstar',
content: 'Bla bla bla'
}
end
test "created article should have the correct attributes" do
post :create, article: valid_attributes
article = Article.last
valid_attributes.keys.each do |key|
assert_equals article[key], valid_attributes[key]
end
end
end
Related
In my Rails 4 project I've got a model, EventGenerator with an instance method generate (which creates some records in the database), and a class method generate_all which is intended to be called by a Rake task and looks something like this:
def self.generate_all
all.each(&:generate)
end
I can think of several approaches to testing this (I'm using RSpec 3 and Fabrication):
Don't bother - it's too simple to require tests. DHH says "Don't aim for 100% coverage". On the other hand, this is going to be called by a rake task, so won't be regularly exercised: I feel like that's a good reason to have tests.
Create a couple of EventGenerator instances in the database and use any_instance.should_receive(:generate) as the assertion - but RSpec 3 now recommends against this and requires a fudge to make it work. This is a personal 'showcase project' so if possible I'd like everything to be best-practice. Also (DHH aside) shouldn't it still be possible to create fast model specs which don't touch the database?
Like 2, but stub out EventGenerator.all to return some instances without touching the database. But stubbing the class under test is bad, right? And fragile.
Don't worry about unit testing it and instead cover it with an integration test: create a couple of generators in the database, run the method, check what gets changed/created in the database as a result.
Change the implementation: pass in an array of instances. But that's just pushing the problem back by a layer, and a test-driven change which I can't see benefitting the design.
Since I can't really think of a downside for option 4, perhaps that's the answer, but I feel like I need some science to back that up.
I would actually not bother to test it (so your 1.) as the method is really trivial.
If you would like to have it under test coverage though I'd suggest you to use your 3. My reasons are as follows:
Your test for .generate_all just needs to assert that the method #generate gets call on every instance returned by .all. It this context the actual implementation of .all is irrelevant and can be stubbed.
Your tests for #generate should assert that the method does the right thing. If these tests assert the proper functioning of this method, there's no need for the tests for .generate_all to duplicate any assertion.
Testing the proper functioning of #generate in the tests for .generate_all leads to unnecessary dependencies between the tests.
On several recent projects I've felt that model associations can get very complex very fast. It feels like testing is way harder than it needs to be because of this complexity. For example, I need to create an instance of model A for a test. Many times, it looks something like this (this is taken from the app I'm working on right now):
Create model A, but model A relies on B.
Model B relies on model C
Model C relies on D, E, and F. Specifically, it needs 6 F's to be attached for it to be considered valid.
Model D, E, and F may have a dependency or two.
Finally, A is created. I am using factories on this application and that helps a bit, but it still feels like too much when I need to satisfy so many validations in order to create a simple model that is not related to any of that.
Stubs might help, but I feel like that requirement represents something wrong with the modelling at it's core.
Are there any patterns designed to help with dependencies like this? One thing I've been thinking about is to make most of the validation conditional based on context. The controllers will save the models in that validation context but that lets my test suite create objects that would otherwise be "invalid" in the live app or full integration test suite. The problem with that is that I feel like it alters my codebase for the sake of testing and I think that is generally a bad idea.
Like you I'd rather not have test-specific code in my application. I'd also rather have validations in place all the time, because if I didn't who knows what test might construct invalid data and thereby pass when it shouldn't?
But it's better when tests run fast. So:
I trust you've already made everything in the database a seed that should be, i.e. model instances that don't need to change more often than you deploy.
If you're using factory_girl, rather than just defining associations and letting factory_girl create them all every time, you could make your factory reuse dependencies:
factory :a do
before :create do |a, _|
a.b ||= B.first || FactoryGirl.create(:b)
end
end
(Haven't tested that but I trust the intention is clear.)
If you need to reuse a B sometimes and make a new one sometimes, obviously you have to write some code sometimes. The most sensible thing to do is probably to create a B in the test and pass it in to the A's that should reuse it and not to the ones that don't.
Is there any way to auto-generate simple test cases? I found myself spending time writing very simple tests that make sure all controllers and models are working fine. Here is an example of controller test case written with rspec:
machine = FactoryGirl.create(:machine, type: 1)
mac = FactoryGirl.create(:mac, machine_id: m1.id)
win = FactoryGirl.create(:win, machine_id: m4.id)
sign_in user
get :index
get :show, id: machine.id
get :report
I cannot find any tool today that can auto-generate such tests based on new written code. If really nothing exists, I may consider building a solution to this problem.
If a test were predictable enough to generate it wouldn't be worth writing. In your example, you don't assert anything. That's a very weak test, good only for raising code coverage. It would be much stronger if it asserted what should be on the page. You can't generate that. You also can't generate a scenario that traverses multiple pages in a meaningful way. (I think your example wants to be an rspec feature spec or a Cucumber scenario.)
It would make sense to write a generator that creates a skeleton that the developer could fill in with the meaningful parts that can't be generated, however.
To cover basic functionality you could write a specific generator.
You can also redefine standard scaffold templates (for example, by adding your own template to lib/templates/rspec/model/model_spec.rb - this will redefine model scaffolding).
But the real question is why to do so. Following TDD you should write the test and then create your code, not vice versa.
I'm seeing some weird behaviour in FactoryGirl that seems to contradict the documentation. In an Rspec test, if I do this;
static_groups = FactoryGirl.create_list(:static_group,5)
expect(StaticGroup.count).to eq(5)
The test fails (expected: 5, got: 0). If I add in explicit saves of the records;
static_groups = FactoryGirl.create_list(:static_group,5)
static_groups.each do |grp|
grp.save
end
expect(StaticGroup.count).to eq(5)
The test passes! I thought "create" in factorygirl was supposed to do a save for you, but in this case it is plainly not saving records which can be saved straight away afterwards! Even weirder, if I interrogate the objects in the first example (where I don't do an explicit save) they all have ids, so they've definately been talking to the database!!
EDIT
Before anyone points out the dodgy "count" syntax, I'm using DataMapper as an ORM, that's a valid way of counting :)
Ugh - nightmare.
Eventually figured out what it was - the "StaticGroup" class uses STI to figure its type out. This is a little more complex because it hooks into two databases and the class structure changed between legacy and new-world.
Long story short, I copy/pasted my group factory into the static group factory and forgot to change the type. There was a hook in the "save" method that sorts this out for me and it wasn't getting fired when factorygirl created the object.
Say I have an instance method that does many different things that I need to test, something like store#process_order. I'd like to test that it sends an email to the customer, adds an entry in the orders table, charges a credit card, etc. What's the best way to set this up in rspec? Currently, I'm using rspec and factory girl I do something like this:
describe Store do
describe "#process_order" do
before do
#store = Factory(:store)
#order = Factory(:order)
# call the process method
#store.process_order(#order)
end
it 'sends customer an email' do
...
end
it 'inserts order to db' do
...
end
it 'charges credit card' do
...
end
end
end
But it feels really tedious. Is this really the right way to write a spec for a method that I need to make sure does several different things?
Note: I'm not interested in answers about whether or not this is good design. It's just an example I made up to help w/ my question - how to write these types of specs.
This is a good method because you can identify which element is broken if something breaks in the future. I am all for testing things individually. I tend not to check things get inserted into the database as you are then rails functionality. I simply check the validity of the object instead.
This is the method that is used in the RSpec book too. I would certainly recommend reading it if you are unsure about anything related to RSpec.
I think what you are doing is fine and I think it's the way rspec is intended to be used. Every statement (specification) about your app gets its own block.
You might consider using before (:all) do so that the order only has to get processed once but this can introduce dependencies on the order the specs are run.
You could combine all the code inside describe "#process_order" into one big it block if you wanted to, but then it would be less readable and rspec would give you less useful error messages when a spec fails. Go head and add raise to one of your tests and see what a nice error message you can get from rspec if you do it the way you are currently doing it.
If you want to test the entire process then we're talking about an integration test, not a unit test. If you want to test #process_order method which does several things, then I'd expect those things mean calling other methods. So, I would add #should_receive expectations and make sure that all paths are covered. Then I would spec all those methods separately so I have a nice unit spec suite for everything. In the end I would definitely write an integration/acceptance spec which checks if all those pieces are working together.
Also, I would use #let to setup test objects which removes dependencies between spec examples (it blocks). Otherwise a failure of one of the examples may cause a failure in other example giving you an incorrect feedback.