How to Test Third Party Gems with Monolith Breakup into Gems?

This message was imported from the Ruby/Rails Modularity Slack server. Find more info in the import thread.

Message originally sent by slack user U78F0G7XH32

Hi! I have a question for anyone who has broken a monolith up into gems. Do you do anything to ensure that those gems are tested with the same versions of third party gems as the application runs with?

Message originally sent by slack user U78EPEM2PY0

At Root, we haven’t done anything special - most of our Rails-provided dependencies don’t specify a version at all, but otherwise we provide liberal version strings where we can. This isn’t really much different than if we were publishing gems for consumption by others, though. The Gemfile.lock is the final say since that’s what’s installed before running our tests in each gem.

This has been one of the biggest pain point of using gems as components. I recently discovered a bug related to PDF form filling (think taxes) where a change in Ruby’s round led to the potential of displaying a dollar amount not as 1234.00 as 12.34.

It doesn’t happen often, but it can bite you… You have three options:
• ignore the problem
• lock down all versions you care about to an exact version in every gem (then bundler won’t let you create incompatible dependencies)
• keep all versions liberal and run the component’s tests against a matrix of all dependency versions that you expect to observe in production

Message originally sent by slack user U78F0G7XH32

Thanks for those explanations!

What we are doing is dynamically generating our Gemfiles by parsing the root Gemfile and Gemfile.lock.

In the dynamic Gemfile we specify exact versions, so they stay in sync with whatever is in the root application Gemfile.lock. Then we gitignore the Gemfile.lock files for our gems, so that we end up with only one place in our repo that specifies the exact versions to use.

It works and its nice because there is a single source of truth for versions, but really it involves a bit too much digging around in Bundler’s internals.

<@U78F0G7XH32> that sounds like a very interesting solution. Might you be able to share the underlying code in some way?

Message originally sent by slack user U78F0G7XH32

I’d have to check about that, but at a very high level, you can parse out the application Gemfile with:

Bundler::Definition.build(gemfile_path, nil, {})

And then parse the lockfile with:

Bundler::LockfileParser.new(Bundler.read_file(gemfile_lock_path))

In your gem’s Gemfile if you have something like:

GemfileBuilder.build(self)

That will pass the bundler DSL to the method and there you can build your Gemfile.