Wondering if someone can guide me on how I might test this code. For me, testing is unexplored terrain, and I’m a little baffled at whether I should use mocks, stubs, or what-have-you.
class BetaWhitelistInterceptor
DOMAIN_WHITELIST = [
'foobar.com',
'boofar.net'
]
def self.delivering_email(message)
Rails.logger.info("BETA SERVER DETECTED. Filtering e-mails through whitelist.")
filtered_to_emails = []
message.to.each do |to|
domain = to.split("@")[1]
filtered_to_emails << to if DOMAIN_WHITELIST.include?(domain)
end
message.to = filtered_to_emails
message.subject = "BETA - #{message.subject}"
message.perform_deliveries = false if message.to.empty?
end
end
Here’s what I have so far for a test case:
class BetaWhitelistInterceptorTest < ActiveSupport::TestCase
test "interceptor filters to-emails" do
message = Minitest::Mock.new
# magic goes here???
BetaWhitelistInterceptor.delivering_email(message)
end
end
I am very lost as to how I would use mocks/stubs in this case. I pretty much just want to test that this method is filtering e-mails based on a whitelist.
It seems like I have to know what class message is going to be, before I can appropriately mock/stub it. I’d like to avoid that if possible, and just use plain objects if I can.
Personally I try to avoid mocks/stubs/etc unless absolutely necessary (3rd party services, etc). The number of times I have seen tests pass and say everything is all good only for it to crash and die in production because of mocks I cannot count.
There is no guarantee your mock is acting like the real code. Therefore you may code the mock to act differently by accident or even if you get it right future changes to the real code may cause it to drift from the behavior you have mocked.
I prefer black-box testing that uses mocks as least as possible. In this case, I would use one of the emails in the system as an example. Have it be sent to a white-list domain and make sure it passes the message through just fine. Then send it to a blacklist domain and make sure it modifies the message as you desire.
The only downside to this approach is if your “example” email ever goes away or changes it might break your test. But that will be obvious when your test suite runs and you can then make adjustments to match the changes or choose a new example.
But now you have more confidence that not only is your interceptor acting like it should you also have confidence it is properly registered with Rails and active. Mocks won’t give you that.
This is how I’d do it in Rspec, not sure about minitest or whatever. I did make a slight change to your #delivering_email to return the message so I could test it directly without mocks. And since I can’t see the implementation of your Message class I used an OpenStruct.
class BetaWhitelistInterceptor
DOMAIN_WHITELIST = [
'foobar.com',
'boofar.net'
]
def self.delivering_email(message)
Rails.logger.info("BETA SERVER DETECTED. Filtering e-mails through whitelist.")
filtered_to_emails = []
message.to.each do |to|
domain = to.split("@")[1]
filtered_to_emails << to if DOMAIN_WHITELIST.include?(domain)
end
message.to = filtered_to_emails
message.subject = "BETA - #{message.subject}"
message.perform_deliveries = false if message.to.empty?
message
end
end
RSpec.describe BetaWhitelistInterceptor, type: :model do
describe ".delivering_email" do
it 'filters non whitelisted domains' do
message = OpenStruct.new to: ['user@foobar.com', 'user@boofar.net', 'other@bad-domain.com']
message = BetaWhitelistInterceptor.delivering_email(message)
expect(message.to).to match_array(['user@foobar.com', 'user@boofar.net'])
end
it 'assigns the correct subject' do
message = OpenStruct.new to: ['user@foobar.com'], subject: 'test subject'
BetaWhitelistInterceptor.delivering_email(message)
expect(message.subject).to eq("BETA - test subject")
end
it 'doesnt perform deliveries without whitelisted recipients' do
message = OpenStruct.new to: ['other@bad-domain.com'], perform_deliveries: true
message = BetaWhitelistInterceptor.delivering_email(message)
expect(message.to).to match_array([])
expect(message.perform_deliveries).to be false
end
end
end