Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Ayush Newatia
Ayush Newatia

Posted on

     

Testing controller concerns in Rails

This post was extracted and adapted fromThe Rails and Hotwire Codex.

Concerns are a great way to organize and de-duplicate code in Rails.

One of my favorite use cases is to abstract everything out of theApplicationController into concerns. This begs the question:how do you test these concerns?

The approach for testing concerns is a bit of a judgement call as they're alwaysmixed in with a class and aren't used on their own. Ideally we'd write tests to verify the behavior of a class which would also test the concern in the bargain.

But what if the concern adds the same functionality to a large number of classes? For example, a concern whichAuthenticates every request. It isn't pragmatic to test each and every controller action for authentication logic. In such cases, I believe the best approach is to test the concern in isolation.

Test harness for controller concerns

Let's stick with the hypothetical example of a concern calledAuthenticate. It's usable only within a controller, so we'll need atest harness to test it independently.

Create asupport/ folder undertest/ and create some files for the test harness within it.

$ mkdir test/support$ touch test/support/test_controller.rb$ touch test/support/routes_helper.rb
Enter fullscreen modeExit fullscreen mode

TheTestController doesn't need to do much by itself. It will be subclassed in individual tests. Each action will render the name of the controller and action which can then be asserted in the test cases.

classTestController<ActionController::Basedefindex;enddefnew;enddefcreate;enddefshow;enddefedit;enddefupdate;enddefdestroy;endprivatedefdefault_renderrenderplain:"#{params[:controller]}##{params[:action]}"endend
Enter fullscreen modeExit fullscreen mode

Next, we need a way to draw some test-only routes to point toTestController subclasses in test cases. The test routes will be scoped to/test so they don't clash with existing routes.

# test/support/routes_helper.rb# Ensure you call `reload_routes!` in your test's `teardown`# to erase all the routes drawn by your test case.moduleRoutesHelpersdefdraw_test_routes(&block)# Don't clear routes when calling `Rails.application.routes.draw`Rails.application.routes.disable_clear_and_finalize=trueRails.application.routes.drawdoscope"test"doinstance_exec(&block)endendenddefreload_routes!Rails.application.reload_routes!endend
Enter fullscreen modeExit fullscreen mode

Thedraw_test_routes helper takes a block which is executed inside the context ofRails.application.routes.draw. In essence, it's doing the exact same thing asconfig/routes.rb, but in the context of the test suite.

Files under thetest/ folder in Rails are notautoloaded, so these files need to berequired andincluded manually.

# test/test_helper.rb# ...Dir[Rails.root.join("test","support","**","*.rb")].each{|f|requiref}# ...classActionDispatch::IntegrationTestincludeRoutesHelpersend
Enter fullscreen modeExit fullscreen mode

With the test harness in place, we can now write some tests for theAuthenticate concern.

$ touch test/controllers/concerns/authenticate_test.rb
Enter fullscreen modeExit fullscreen mode
require'test_helper'classAuthenticateTestsController<TestControllerincludeAuthenticatedefshow# ...endendclassAuthenticateTest<ActionDispatch::IntegrationTestsetupdodraw_test_routesdoresource:authenticate_test,only:[:new,:create,:show,:edit]endendteardowndoreload_routes!end# ...# Test cases go here ...# ...end
Enter fullscreen modeExit fullscreen mode

The test-specificAuthenticateTestsController strips away any peripheral functionality and enables us to focus on testing the code inAuthenticate. It can now be tested just like any other controller!

If you liked this post, check out my book,The Rails and Hotwire Codex, to level-up your Rails and Hotwire skills!

Top comments(1)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
epigene profile image
Augusts Bautra
Senior Rails developer from Latvia.Avid gamer. Longevity enthusiast. #keto-dude
  • Location
    Riga, Latvia
  • Education
    College, Unfinished
  • Work
    Software Engineer at UPB AS
  • Joined

An excellent writeup, Ayush!
Testing mixins in isolation, as if we were writing them in a gem without any real implementers yet, is key and should be applied irrespective of the number of places used, as I expound indev.to/epigene/the-alternative-to-...

One avenue of improvement, especially in RSpec context, would be to avoid defining a named class withclass AuthenticateTestsController < TestController which pollutes the object space and can lead to subtle bugs due to collision, and instead define an anonymous class and usestub_const to attach the class to the constant for just that one example.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a freelance web developer and Rubyist. I'm the author of The Rails and Hotwire Codex (https://railsandhotwirecodex.com) and the co-host of Just A Spec.
  • Location
    London, UK
  • Joined

More fromAyush Newatia

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp