class: center, middle # The Doctrine of
Useful Objects ## Get rid of mocking --- # Who am I? .pull-left.avatar[![Picture of me](https://avatars.githubusercontent.com/u/945115?v=4)] - Chris Wawer - Ruby on Rails Developer - Improving legacy applications - https://wawer.it --- # Service Object ```ruby class FindMovie def initialize(title) @title = title end def call Tmdb::Movie.find(@title) end end ``` --- class: center, middle # Unit Testing --- # External dependencies Dealing with external dependencies: ```ruby # stubbing allow(Tmdb::Movie).to receive(:find).with(...) # mocking movie_service = double(:movie_service) allow(movie_service).to receive(:find).with(...) ``` --- class: center, middle
--- # Usefule Objects - dependency injection - single responsibility - ... --- # Service Object ```ruby class FindMovie def initialize(title, movie_service) @title = title @movie_service = movie_service end def call @movie_service.call(@title) end end ``` Everything OK? --- # Incorrect DI ```ruby class FindMovie def initialize(title, movie_service) @title = title @movie_service = movie_service end def call @movie_service.call(@title) end end FindMovie.new("Ruby on Rails: The Documentary", nil) ``` --- # Usefule Objects - dependency injection - single responsibility - don't allow to intialize object with empty dependency (1st law) - ... --- # Service Object ```ruby class FindMovie attr_reader :title dependency :movie_service, MovieService def self.build(title) new(title).tap do |instance| MovieService.configure(instance) end end def initialize(title) @title = title end def call movie_service.call(title) end end ``` --- # "Empty instance" ```ruby find_movie = FindMovie.new find_movie.movie_service # => #
find_movie.movie_service.call(title) # => #
find_movie.movie_service.invoked?(:call) ``` --- class: center, middle # Substitutes --- # Substitutes ```ruby class FindMovie dependency :movie_service, MovieService end class MovieService # ... module Substitute def self.build MovieService.new end class MovieService < ::MovieService def call # ... end def set(result) # ... end end end end ``` --- # Substitutes ```ruby class FindMovie dependency :movie_service, MovieService end class MovieService module Substitute def self.build MovieService.new end class MovieService < ::MovieService include Mimic::Recorder::Predicate record def call # ... end def set(result) # ... end end end end ``` --- class: center, middle # Questions ???