Encapsulation and Testing in Ruby
In object-oriented applications, there are two types of messages: Query and Command. Commands do not return a value but instead create side effects in the same or different component. Queries, on the other hand, produce a value without changing anything about the system. The crucial difference between these two message types is the creation of side effects.
Encapsulation involves bundling data with the methods that operate on that data. The “Tell, don’t ask” principle lies at the heart of information hiding. The tricky part of this principle is assuming that an object knows what to “say” or what answers it needs to provide.
In Ruby, a practical technique for enforcing encapsulation is to always return ‘self’ from any method that modifies the state. Returning ‘self’ at the end of each Command method ensures that objects own their internal data and that every method is a Query method since the last statement a method executes represents its return value. To promote “Tell, don’t ask,” the most effective way is to make objects the owners of their data. Objects are responsible for exposing methods to consume information from their internals and may query themselves.
Factories are exempt from the other rules since the layer in which Factories are defined is not yet an instance of any object, so there is no ‘self’ yet. Since there is no instance in memory, there is also no preserved state within the object.
In object-oriented programming, an object’s incoming messages are outgoing messages of another object. Therefore, the object receiving the message should be responsible for testing its API. Testing outgoing query messages from a consumer duplicates the test, so it is unnecessary to test them, make assertions about their result, or expect to send them. Refrain from asserting what they return, and don’t set expectations that you’ll send them.
[¹]: Practical Object-Oriented Design: An Agile Primer Using Ruby