given I have the following with the internal assignments to filter and ldap: class Collaborator def self.search_ldap(term) last_name = term.strip.split(/[, ]/).first.downcase filter = Net::LDAP::Filter.eq(''surName'', last_name) ldap = Net::LDAP.new( :host => ''foo'', :port => 636, :base => ''bar'', :encryption => :fiz) ldap.search(:filter => filter) end end I would like mock out the LDAP class so i can test the behavior of the method without LDAP. describe Collaborator do it "should search ldap and return last names from the first item of the search phrase" do entry = Net::LDAP::Entry items = {:cn=>["Jim Bob"],:uid=>[''bob46''],:mail=>''bob at life.edu''} items.each{|k,v| entry.stub(k) {v} } # how do I get to objects inside this method, something like? assigns(:ldap).stub(:search).and_return(entry) # but that doesnt work Collaborator.search_ldap("Bob").should == [entry] end end -- Jed Schneider -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110621/acdb7769/attachment.html>
J. B. Rainsberger
2011-Jun-21 21:56 UTC
[rspec-users] stubbing /mocking ldap within a method
On Tue, Jun 21, 2011 at 17:00, Jed Schneider <jed.schneider at gmail.com> wrote:> given I have the following with the internal assignments to filter and ldap: > class Collaborator > ??def self.search_ldap(term) > ?? ?last_name = term.strip.split(/[, ]/).first.downcase > ?? ?filter = Net::LDAP::Filter.eq(''surName'', last_name) > ?? ?ldap = Net::LDAP.new( :host => ''foo'', :port => 636, :base => ''bar'', > :encryption => :fiz) > ?? ?ldap.search(:filter => filter) > ??end > end > I would like mock out the LDAP class so i can test the behavior of the > method without LDAP.In this case, I''d invoke the rule "Don''t mock types you don''t own". What can you trust? LDAP does the right thing. What can you get wrong? Sending the wrong parameters to LDAP. Therefore, check the parameters you send to LDAP. class Collaborator def initialize(ldapGateway = LdapGateway.new) @gateway = ldapGateway end def self.search_ldap(term) last_name = term.strip.split(/[, ]/).first.downcase @gateway.search(''surName'', last_name, ''foo'', 636, ''bar'', :fiz) end end class LdapGateway def search(label, value, host, port, base, encryption) filter = Net::LDAP::Filter.eq(''surName'', last_name) ldap = Net::LDAP.new( :host => ''foo'', :port => 636, :base => ''bar'', :encryption => :fiz) ldap.search(:filter => filter) end end Judging only by the names, "label" and "value" seem separate from "host"?"encryption". My intuition tells me to move the latter group into constructor parameters. class Collaborator def initialize(ldapGateway) @gateway = ldapGateway end def self.search_ldap(term) last_name = term.strip.split(/[, ]/).first.downcase @gateway.search(''surName'', last_name) end end class LdapGateway def initialize(host, post, base, encryption) # assign to fields end def search(label, value) filter = Net::LDAP::Filter.eq(''surName'', last_name) # SMELL Temporal duplication; can we create an LDAP in the constructor? ldap = Net::LDAP.new( :host => @host, :port => @port, :base => @base, :encryption => @encryption) ldap.search(:filter => filter) end end Now the LdapGateway responds to search(label, value), so I don''t think Collaborator needs to know that any LDAP is going on. class Collaborator def initialize(term_repository) @term_repository = term_repository end def self.search_ldap(term) last_name = term.strip.split(/[, ]/).first.downcase @term_repository.search(''surName'', last_name) end end I''m using the mechanical name "Repository" here, because I don''t yet understand the role this object plays in the system. class LdapGateway # I''m a TermRepository! Extract a module someday? def initialize(host, post, base, encryption) # assign to fields end def search(label, value) filter = Net::LDAP::Filter.eq(''surName'', last_name) # SMELL Temporal duplication; can we create an LDAP in the constructor? ldap = Net::LDAP.new( :host => @host, :port => @port, :base => @base, :encryption => @encryption) ldap.search(:filter => filter) end end Now it''s easy to check: describe Collaborator do context "when searching" do it "should pass the proper surname to the TermRepository" do termRepository = mock("a TermRepository") termRepository.should_receive(:search).with(''surName'', ''rainsberger'') Collaborator.new(termRepository).search(''J. B. Rainsberger'') end it "should return the repository''s results" do Collaborator.new(stub(:search => ["a", "b", "c"])).search(''J. B. Rainsberger'').should == ["a", "b", "c"] end end end Does that help you? -- J. B. (Joe) Rainsberger :: http://www.jbrains.ca :: http://blog.thecodewhisperer.com Author, JUnit Recipes Free Your Mind to Do Great Work :: http://www.freeyourmind-dogreatwork.com
Joe, Here is the solution I decided to go with, for now: describe Collaborator do describe "class_methods" do subject {Collaborator} it { should respond_to(:search_ldap).with(1).argument } it "should format the search term" do subject.prepare_search_term("Bob").should == "bob" end end end describe LdapGateway do subject { LdapGateway.new("surName") } it { should respond_to(:search_for).with(1).argument } it "should set @ldap to an instance of Net::LDAP" do subject.instance_eval(''@ldap'').should be_kind_of(Net::LDAP) end it "should set the label to the initialzied variable" do subject.instance_eval("@label").should == "surName" end end class LdapGateway def initialize(label) @ldap = Net::LDAP.new(credentials) @label = label end def search_for(term) @ldap.search(:filter => Net::LDAP::Filter.eq(@label, term)) end end class Collaborator < ActiveRecord::Base def self.search_ldap(term) last_name = prepare_search_term(term) @gateway = LdapGateway.new(''surName'') @gateway.search_for(last_name) # alternatively, maybe # LdapGateway.new(''surName'').search_for(last_name) end def self.prepare_search_term(term) term.strip.split(/[, ]/).first.downcase end end -- Jed Schneider 706-207-4458 (m) 803-386-7214 jedschneider.posterous.com twitter.com/jedschneider github.com/jedschneider -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110622/5544e7d2/attachment.html>