Unit testing models with relationships between tables.

Hi,

I'm quite new to Rails but even newer to the idea of unit testing as all the things I have done so far have been relatively small and so I haven't bothered with unit tests. I'm just embarking on a much bigger project and wanted some advice as to testing strategies.

I've been following the testing chapter in Agile Web Development With Rails by the Pragmatic Programmers and I've been getting on ok with that but I'm not sure how to test models that have relationships between them.

For instance I have one model, ModelA say that belongs to another, ModelB. I would like to use fixtures to preload some data; how do I get the unit test for ModelA to load the fixture data for ModelB so that I can perform the tests with the table dependencies in place? How do I specify the foreign key value in the ModelA fixture so that it refers to a record in ModelB?

What are the strategies when it comes to testing models with relationships between tables? Do you just test as if there was no relationship and use dummy values for foreign key fields or should the relationships also be tested?

Also on a separate note, is there any point testing all the default validations implemented by rails when they are themselves tested as part of the rails framework?

Thanks in advance, Toby

tobyclemson@gmail.com wrote:

I'm quite new to Rails but even newer to the idea of unit testing as all the things I have done so far have been relatively small and so I haven't bothered with unit tests. I'm just embarking on a much bigger project and wanted some advice as to testing strategies.

Read everything I wrote over the past decade (including the comics) (-;

For instance I have one model, ModelA say that belongs to another, ModelB. I would like to use fixtures to preload some data; how do I get the unit test for ModelA to load the fixture data for ModelB so that I can perform the tests with the table dependencies in place?

In the fixtures, specify low numbers for each item's id. Then copy the matching numbers into other fixtures, so model_b_id: 2 refers to the model_bs.yml with id: 2.

(This data duplication is not DRY; this is a known issue with fixtures that someone might fix someday.)

How do I specify the foreign key value in the ModelA fixture so that it refers to a record in ModelB?

Then put fixtures :model_as, :model_bs in the top of your test suite, and test like this:

   ma = model_as(:my_model_a)    assert_equal 'foo', ma.model_b.name

That's it. The .model_b spontaneously populates when you call it.

What are the strategies when it comes to testing models with relationships between tables? Do you just test as if there was no relationship and use dummy values for foreign key fields or should the relationships also be tested?

You need tests that resist bugs when you refactor your code. (This makes refactoring easier, which makes your initial design less important!)

The best way to test is "test first", so you write a test that fails because your model_a can't do something yet. Then you upgrade model_a to pass the test. If model_a requires a model_b to get its job done, then the test will check that the job is done, and this forces model_a and model_b to have some relationship.

Done right, you won't test the navigation itself between A and B. So you are free to refactor that navigation (such as a migration that converts has_one to has_many), and if the test still passes, then A and B still get their job done.

A test case should follow the AAA pattern:

   def test_case        a = model_as(:my_a) # Assemble test targets        q = a.perform # Activate the target method        assert_equal 42, q # Assert the result    end

Most tests should call a .perform method that actually does something. The original rule for this was "don't test getters and setters". That means don't test trivial methods that merely fetch data. These include your has_many relationships. Only test methods that add business value to your app, and these methods will test the getters and setters for you.

Also on a separate note, is there any point testing all the default validations implemented by rails when they are themselves tested as part of the rails framework?

Are you going to refactor the rails framework?