-
-
Save rilian/0f744e2f102349170351 to your computer and use it in GitHub Desktop.
describe ClassName do | |
describe 'Database' do | |
end | |
describe 'Relations' do | |
end | |
describe 'Validations' do | |
end | |
describe 'Scopes' do | |
end | |
describe 'Class methods' do | |
end | |
describe 'Callbacks' do | |
end | |
describe 'Instance Methods' do | |
end | |
end |
В предложенном варианте покрытие, думаю, будет хорошим. НО
- мы тестируем внутреннюю реализацию модели (например в жизни scope может превратиться в метод и наоборот)
- тесты будут хрупкими
- тесты могут не поспевать за рефакторингом
- тестируется отдельные методы и побочные эффекты; нет сценариев практического использования модели
- тестируется низлежащий слой за который отвечает фреймворк; например relations.
Боюсь тут за деревьями леса не разглядеть.
Вообщем я бы в первую очередь тестировал внешний интерфейс модели и сценарии ее использования, не нарушая инкапсуляцию оной.
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme LateBinding of all things.
— Alan Kay
Вот неплохое выступление с RubyC на тему: https://www.youtube.com/watch?v=Vb8ybJuL2Xw
thanks for comments, good ideas
I fully agree with saaji
This applies only to model tests, as testing of objects that do not depend on database or other 3rd-party code is a completely different story with a lot more advanced tools available to organise code and tests.
As a small compromise we can imagine that any model is actually 2 objects: Class and Instance, thus they may have separate sections.
Main reason is to minimise maintenance cost of tests but decreasing test fragility.
"Fragile Tests increase the cost of test maintenance by forcing us to visit many more tests each time we modify the functionality of the system or the fixture. It is particularly deadly on projects that do highly incremental delivery" - http://xunitpatterns.com/Fragile%20Test.html
-
Tests that over-specify implementation are fragile because they will fail if we change implementation mechanism. Examples of that are refactoring of scope to class method or callback to wrapper method.
-
It is not necessary. As @rilian told, sections are needed to locate tests for big models. Big models is a problem that should be fixed. Tests that are difficult to locate because there are too many of them is only a symptom of this problem, they should not cover it.
-
They force to write separate tests for every method. But methods may be a result of refactoring, thus they will be covered in a separate group of tests, possibly for another object. If we then write additional tests for this particular method, it again creates a problem of fragile tests - we'd have to change multiple tests if we change 1 method.
@rilian has mentioned that if we remove the original code under test, we will be left with separated methods without any tests. While it is true, that is not a big problem. When we remove code, tests for that code will fail. We will then remove tests.
- If separated methods now do not have any tests, test coverage will decrease thus showing us that problem. This will require to write tests for these methods
- if test coverage does not decrease, then they're covered in other tests thus nothing should be changed.
describe ClassName do
# i'd not use Methods, since any behaviour may be implemented with a number of mechanisms
# that are not methods: delegates, scopes, Proc, anything else
describe 'Class' do
end
# the other reason is for not using methods is that they often cannot be tested in isolation,
# for example Stack may have only 2 methods: #pop and #push without any visible state.
# You cannot test #push in isolation so you'd write usage scenarios, not method tests
describe 'Instance' do
# any section may or may not have any additional sections
# it may be reasonable to add
describe 'Validations'
# but not necessary - there may be only 1 validation test and separate section for it would clutter code
# additionally, validations may be tested in usage scenarios
describe 'Callbacks' # anything 'callbacky' that can be implemented by callbacks, database triggers, overriding instance methods, etc
# it does not make sense to add Relations section, as, they usually are a configuration for 3rd party code.
# In case when they are complex, they can be tested, but should not get any specific treatment or section
# The reason for that is relations can be implemented with any different mechanisms, including regular methods. The same is true for methods - they can be refactored to relations.
end
# It does not make any sense to test internal implementation of database as that would make tests fragile
# but as @rilian really wants to put requests for future implementation changes in test section, we've agreed to have something like
describe 'Database' do
# but it will not have any actual tests - only pending ones
# as not to break any deployment
end
end
Clean version:
describe ClassName do
describe 'Class' do
end
describe 'Instance' do
describe 'Validations'
describe 'Callbacks'
end
describe 'Database'
end
improved
describe 'Class' do
pending
end
describe 'Instance' do
pending
end
describe 'Validations' do
pending
end
describe 'Callbacks' do
pending
end
# TODO: move to a spec/database folder
describe 'Database' do
pending
end
spec/models/<model_name>/database_spec.rb
spec/models/<model_name>/relations_spec.rb
spec/models/<model_name>/validation_spec.rb
spec/models/<model_name>/methods_spec.rb