简体   繁体   中英

How can I use mock in my rails minitest?

There are two classes, UserDevice(< ActiveRecord::Base) and NotificationAdapter.
In NotificationAdapter, I use AWS SDK, and UserDevice uses NotificationAdapter instance.
The Joint point is below,

protected def notificationAdapter
  @notificationAdapter ||= NotificationAdapter.new(self)
end

In UserDevice test, I want to make temporary NotificationAdapter mock for replacing the original NotificationAdapter, and use this mock only in the test.
But I don't know how to do, because this is my first case using mock in test.

I think it requires two steps below,

  1. Make temporary NotificationAdapter class(NorificationAdapterMock) in test code.

    NotificationAdapterMock = MiniTest::Mock.new mock.expect :setEndpoint, 'arn:testEndpoint' mock.expect :subscribeToAnnouncement, true

  2. Change notificationAdapter method of UserDevice to below,

    protected def notificationAdapter @notificationAdapter ||= NotificationAdapterMock end

But I don't know whether it is right or wrong. What should I do?

You need to

  1. mock your NotificationAdapter , so it doesn't fire the network but instead does something safe and easy
  2. And also make sure that your adapter is correctly wired

Side note: Please, follow ruby style guidelines , method names and variables should be written with snake case and not camel case.

So, we may write it this way:

require 'minitest/autorun'

class NotificationAdapter
  def initialize(device)
    @device = device
  end

  def notify
    # some scary implementation, that we don't want to use in test
    raise 'Boo boo!'
  end
end

class UserDevice
  def notification_adapter
    @notification_adapter ||= NotificationAdapter.new(self)
  end

  def notify
    notification_adapter.notify
  end
end

describe UserDevice do
  it 'should use NotificationAdapter for notifications' do
    device = UserDevice.new

    # create mock adapter, that says 'ohai!' on notify
    mock_adapter = MiniTest::Mock.new
    mock_adapter.expect :notify, 'ohai!'

    # connect our mock, so next NoficationAdapter.new call will return our mock
    # and not usual implementation
    NotificationAdapter.stub :new, mock_adapter do
      device.notify.must_equal 'ohai!'
    end
  end
end

More information can be found in MiniTest's documentation on mocks and stubs .

But let's don't stop here! I advise you to move your business logic out from your ActiveRecord models to a separate service class. This will have following good effects:

  1. Your models become thinner
  2. Feature is nicely encapsulated in its own class and obeys Single Responsibility Principle now
  3. Your tests become blazingly fast, because you don't need to load Rails and can mock your model instead.

Here it is:

require 'minitest/autorun'

# same adapter as above
class NotificationAdapter
  def initialize(device)
    @device = device
  end

  def notify
    raise 'Boo boo!'
  end
end

class UserDevice
# the logic has been moved out
end

class NotifiesUser

  def self.notify(device)
    adapter = NotificationAdapter.new(device)
    adapter.notify
  end
end

describe NotifiesUser do
  it 'should use NotificationAdapter for notifications' do
    # Let's mock our device since we don't need it in our test
    device = MiniTest::Mock.new

    # create mock adapter, that says 'ohai!' on notify
    mock_adapter = MiniTest::Mock.new
    mock_adapter.expect :notify, 'ohai!'

    # connect our mock, so next NoficationAdapter.new call will return our mock
    # and not usual implementation
    NotificationAdapter.stub :new, mock_adapter do
      NotifiesUser.notify(device).must_equal 'ohai!'
    end
  end
end

Have a nice day!

PS If you want to know more about testing in isolation, mocking techniques and general "fast rails tests" movement I highly recommend you Gary Bernhardt's destroyallsoftware.com screencasts. This is paid stuff, but it very worths it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM