In Rails models we usually have attributes and relations tests, like:
describe 'attributes' do
it { is_expected.to have_db_column(:identifier).of_type(:uuid) }
it { is_expected.to have_db_column(:content).of_type(:jsonb) }
it { is_expected.to have_db_column(:created_at).of_type(:datetime) }
end
describe 'relations' do
it { is_expected.to belong_to(:user).class_name('User') }
end
And using a TDD style it seems to be some useful tests, however I have been dwelling if these are really necessary tests, and I would like to know if there is some common knowledge about it, is it good practice to create these tests? or are we just testing rails?
Amongst the purposes of a unit test are...
If it's a promise, if other things rely on it, you should test it to ensure you keep that promise. This is regression testing.
But don't test more than you promise. You'll be stuck with it, or your code will break when you make an internal change.
For example...
it { is_expected.to have_db_column(:identifier).of_type(:uuid) }
This promises that it has a column called identifier
which is a UUID. Usually you don't promise all that detail; it is glass-box testing and it makes your test brittle.
Instead, promise as little as you can. Its ID is a UUID. This is black-box testing.
require "rspec/uuid"
describe '#id' do
subject { thing.id }
let(:thing) { create(:thing) }
it 'has a uuid ID' do
expect(thing.id).to be_a_uuid
end
end
It's possible there is an even higher level way to express this without holding yourself specifically to a UUID.
it { is_expected.to have_db_column(:content).of_type(:jsonb) }
Similarly, don't promise it has a jsonb column. That is blackbox testing. Promise that you can store complex data structures.
describe '#content' do
subject { create(:thing) }
it 'can round trip complex data' do
data = [1, { two: 3, four: [5] }]
thing.update!(content: data)
# Force it to re-load content from the database.
thing.reload
expect(thing.content).to eq data
end
end
it { is_expected.to belong_to(:user).class_name('User') }
Instead of promising what it belongs to, promise the relationship.
describe '#user' do
let(:thing) { create(:thing) }
let(:user) { create(:user) }
before {
user.things << thing
}
it 'belongs to a user' do
expect(thing.user).to eq user
expect(user.things).to contain(thing)
end
end