{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/testing-rails-gems/","result":{"data":{"site":{"siteMetadata":{"title":"The Official MailPace Blog","author":"Paul, founder of MailPace"}},"mdx":{"id":"2bbb49a4-f7f0-5ca7-9450-1465a0c442d0","excerpt":"Rails changes, our tests stay the same? Recently we fixed a couple of bugs in our Rails Gem, ran our tests and published a new gem to RubyGems. When we updated…","body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Testing Rails Gems against different versions of Rails\",\n  \"date\": \"2021-04-08T19:00:03.284Z\",\n  \"description\": \"How we discovered the need and found a way to test our Rails Gem against different versions of Rails\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Rails changes, our tests stay the same?\"), mdx(\"p\", null, \"Recently we fixed a couple of bugs in our Rails Gem, ran our tests and published a new gem to RubyGems. When we updated our Rails apps with the new gem everything worked well, but suddenly some users started reporting errors, and they needed to rollback to an earlier version.\"), mdx(\"p\", null, \"Upon closer inspection we saw that the issue stemmed from some of the new methods we used for the bug fixes not being available in Rails 5.x. Quickly we patched the Gem by replacing those new methods with standard Ruby code, ran our tests and all was well again.\"), mdx(\"p\", null, \"But our tests were only running against Rails 6.1. No matter how well written, they couldn\\u2019t ever catch these bugs. \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"So how do we run these tests against an older version of Rails? And how do we keep doing this in a repeatable way?\")), mdx(\"h2\", null, \"How we test the Rails Gem\"), mdx(\"p\", null, \"Our Rails Gem is structured with a dummy rails project embedded inside it that our tests run against. When we run our tests it loads up this project, sets MailPace as the default delivery method and runs our tests in the context of the dummy rails app.\"), mdx(\"p\", null, \"To test against different versions of Rails, we need an easy way to change this dummy app\\u2019s version of Rails, and then re-run our tests against it. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"If we\\u2019d done this for Rails 5.x earlier, we would have caught the bugs above! \\uD83E\\uDD26\")), mdx(\"h2\", null, \"Changing the Rails Version during tests\"), mdx(\"p\", null, \"One obvious answer is to generate lots of different dummy apps and run tests against each of them, but that\\u2019s not very DRY, and it means it\\u2019s hard to test different versions on the fly. Unfortunately there are many differences between Rails versions, so its not as simple as changing one variable, there\\u2019s a little more to it (but not much more as we shall see).\"), mdx(\"p\", null, \"Within our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".gemspec\"), \" file we specify a development dependency on Rails, which is merged/installed alongside our gemfile when \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bundle install\"), \" is executed when installing our development/test environment:\"), mdx(\"pre\", {\n    \"className\": \"grvsc-container default-dark\",\n    \"data-language\": \"ruby\",\n    \"data-index\": \"0\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"grvsc-code\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"spec.add_development_dependency \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"rails\\\"\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \", \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\">=6.1.3.1\\\"\")))), mdx(\"p\", null, \"Bingo! \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"We just need to modify this version on each re-run of the full test suite.\")), mdx(\"p\", null, \"We use \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://circleci.com/\"\n  }, \"Circle CI\"), \" for testing across our projects at MailPace to ensure our tests are passing on every push and give a public view on our open source components. For our Node.js library we use different Node.js versioned Docker images for each job, however in Rails the Rails version is defined by the project in the Gemfile, which means we can\\u2019t use a different Docker image to switch Rails version.\"), mdx(\"h1\", null, \"Environment Variables to the Rescue\"), mdx(\"p\", null, \"With CircleCI you can specify environment variables in the config, like this:\"), mdx(\"pre\", {\n    \"className\": \"grvsc-container default-dark\",\n    \"data-language\": \"yaml\",\n    \"data-index\": \"1\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"grvsc-code\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"environment\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \":\")), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"  \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"RAILS_TEST_VERSION\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \": \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"5.2.5\\\"\")))), mdx(\"p\", null, \"So we created several CircleCI jobs, each with a different \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"RAILS_TEST_VERSION\"), \" to test against.\"), mdx(\"p\", null, \"Then we use some string interpolation to read from this variable (or default to version 6.1.3.1) when installing Rails for the Dummy project:\"), mdx(\"p\", null, \"In \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mailpace-rails.gemspec\"), \": \"), mdx(\"pre\", {\n    \"className\": \"grvsc-container default-dark\",\n    \"data-language\": \"ruby\",\n    \"data-index\": \"2\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"grvsc-code\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"spec.add_development_dependency \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"rails\\\"\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \", \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"#{\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk12\"\n  }, \"ENV\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"'RAILS_TEST_VERSION'\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"] || \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"'>=6.1.3.1'\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"\")), \"\\n\", mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }))), mdx(\"p\", null, \"Finally, we have to modify our dummy project slightly to use the default Application config for the right Rails version:\"), mdx(\"p\", null, \"Inside \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"test/dummy/config/application.rb\"), \":\"), mdx(\"pre\", {\n    \"className\": \"grvsc-container default-dark\",\n    \"data-language\": \"ruby\",\n    \"data-index\": \"3\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"grvsc-code\"\n  }, mdx(\"span\", {\n    parentName: \"code\",\n    \"className\": \"grvsc-line\"\n  }, mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"config.load_defaults \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"#{\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \" \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk12\"\n  }, \"ENV\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"'RAILS_TEST_VERSION'\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"] ? \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk12\"\n  }, \"ENV\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"[\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"'RAILS_TEST_VERSION'\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"][\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk7\"\n  }, \"0\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"..\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk7\"\n  }, \"2\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \"] : \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"'6.0'\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk1\"\n  }, \" \"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk4\"\n  }, \"}\"), mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"mtk8\"\n  }, \"\\\"\")))), mdx(\"p\", null, \"Which simply says, if the environment variable is set, use defaults from the first three characters (e.g. 5.2.1 becomes 5.2), or use version 6.0.\"), mdx(\"p\", null, \"Now when we run our CircleCI job, we can pick any version of Rails we want just by changing the Environment Variable in the Circle CI config.\"), mdx(\"p\", null, \"All the source code is available here: \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/mailpace/mailpace-rails\"\n  }, \"https://github.com/mailpace/mailpace-rails\")), mdx(\"p\", null, \"And you can see the pipelines here: \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://app.circleci.com/pipelines/github/mailpace/mailpace-rails\"\n  }, \"https://app.circleci.com/pipelines/github/mailpace/mailpace-rails\")), mdx(\"style\", {\n    \"className\": \"grvsc-styles\"\n  }, \"\\n  .grvsc-container {\\n    overflow: auto;\\n    -webkit-overflow-scrolling: touch;\\n    padding-top: 1rem;\\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\\n    padding-bottom: 1rem;\\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\\n    border-radius: 8px;\\n    border-radius: var(--grvsc-border-radius, 8px);\\n    font-feature-settings: normal;\\n  }\\n  \\n  .grvsc-code {\\n    display: inline-block;\\n    min-width: 100%;\\n  }\\n  \\n  .grvsc-line {\\n    display: inline-block;\\n    box-sizing: border-box;\\n    width: 100%;\\n    padding-left: 1.5rem;\\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\\n    padding-right: 1.5rem;\\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\\n  }\\n  \\n  .grvsc-line-highlighted {\\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\\n  }\\n  \\n  .default-dark {\\n    background-color: #1E1E1E;\\n    color: #D4D4D4;\\n  }\\n  .default-dark .mtk1 { color: #D4D4D4; }\\n  .default-dark .mtk8 { color: #CE9178; }\\n  .default-dark .mtk4 { color: #569CD6; }\\n  .default-dark .mtk12 { color: #9CDCFE; }\\n  .default-dark .mtk7 { color: #B5CEA8; }\\n\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Testing Rails Gems against different versions of Rails","date":"April 08, 2021","description":"How we discovered the need and found a way to test our Rails Gem against different versions of Rails"}}},"pageContext":{"slug":"/testing-rails-gems/","previous":{"fields":{"slug":"/tag-emails-with-ohmysmtp/"},"frontmatter":{"title":"MailPace now supports tagging emails 🏷️"}},"next":{"fields":{"slug":"/send-transactional-emails-with-php/"},"frontmatter":{"title":"Send transactional emails using PHP"}}}},"staticQueryHashes":["1956864136","63159454"]}