When doing test setup, is it better to use low-level APIs to get my system in the right state, or use the same abstractions that my application would use? I'm specifically interested in what promotes long-term maintainability in large codebases.
Example (in Ruby, but my question is language-agnostic):
A User can buy an Item using the Sale class:
class User
attr_accessor :join_source, :status
def web_join!
@join_source = 'web'
@status = 'rookie'
end
def promote!
@status = 'veteran'
end
def web_rookie?
join_source == 'web' && status == 'rookie'
end
end
class Sale
attr_reader :item, :user
def initialize(item, user)
@item = item
@user = user
end
def finalize
item.owner == user
if user.web_rookie?
Email.send_web_rookie_receipt(user)
end
end
end
In testing Sale, I will need to create a user in various states. I can either user low-level APIs:
user = User.new
user.join_source = 'web'
user.status = 'rookie'
# test that the send_web_rookie_receipt goes out
user.status = 'veteran'
# test that the send_web_rookie_receipt does not goes out
Or the API that User exposes:
user = User.new
user.web_join!
# test that the send_web_rookie_receipt goes out
user.promote!
# test that the send_web_rookie_receipt does not goes out
To clarify, I am not asking about testing User
in this scenario. I am asking about my tests for Sale
, and whether the setup for testing Sale
should use more abstract methods on User
(such as `promote!) or more concrete, low level methods (attribute assignment).