-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ActionCable channels unit-testing #27191
Conversation
Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @chancancode (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. This repository is being automatically checked for code quality issues using Code Climate. You can see results for this analysis in the PR status below. Newly introduced issues should be fixed before a Pull Request is considered ready to review. Please see the contribution instructions for more information. |
c4a2df1
to
16120f7
Compare
@kaspth |
Thanks, I'll take a look tomorrow 😄 |
def initialize(name) | ||
super "Unable to determine the channel to test from #{name}. " + | ||
"You'll need to specify it using tests YourChannel in your " + | ||
"test case definition" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definition.
end | ||
end | ||
|
||
# Superclass for Action Cable channels functional tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
channel
# assert "chat_1", streams.last | ||
# end | ||
# | ||
# def test_subscribed_without_room_number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test_does_not_subscribe_without_room_number
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, this is about what I made it to tonight. I'll look more in the coming days.
end | ||
|
||
class ConnectionStub | ||
attr_reader :transmissions, :identifiers, :subscriptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stick the broadcasts naming: transmissions
-> broadcasts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can be a confusion: transmissions
list here contains messages transmitted to the socket directly, not through broadcasting.
end | ||
|
||
def logger | ||
@logger ||= ::ActiveSupport::Logger.new(StringIO.new) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't the logger need to support ActiveSupport::TaggedLogging
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe we should reuse new_tagged_logger
from the actual Connection.
end | ||
|
||
@subscriptions = ActionCable::Connection::Subscriptions.new(self) | ||
@identifiers = identifiers.keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we reuse this?
rails/actioncable/lib/action_cable/connection/subscriptions.rb
Lines 56 to 58 in 06d2049
def identifiers | |
subscriptions.keys | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are different identifiers: here we accessing the method argument (within a channel instance).
include ActiveSupport::Testing::ConstantLookup | ||
|
||
included do | ||
class_attribute :_channel_class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why prepend the underscore here?
attr_reader :subscription, :connection | ||
delegate :transmissions, to: :connection | ||
delegate :streams, to: :subscription | ||
setup :setup_connection_stub |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this setup? Isn't it more likely users will need to make their own connection_stub with the needed identifiers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, the case of connection w/o identifiers is very unlikely.
|
||
included do | ||
class_attribute :_channel_class | ||
attr_reader :subscription, :connection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new line before
ActiveSupport.run_load_hooks(:action_cable_channel_test_case, self) | ||
end | ||
|
||
module ClassMethods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there precedent for adding this in the Behavior
module? I'd rather we just force people to call self.channel_class = :some_channel_class
and then move the lookup behavior there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been using ActionMailer::TestCase
and ActionController::TestCase
as examples.
So, I've tried to preserve kind of consistency)
The same goes for #27191 (comment) and #27191 (comment) .
channel = determine_constant_from_test_name(name) do |constant| | ||
Class === constant && constant < ActionCable::Channel::Base | ||
end | ||
raise NonInferrableChannelError.new(name) if channel.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't something people are likely to call on their own. So why don't we just let the nil
through and let it be caught in tests
?
def test_stream_with_params | ||
subscribe id: 42 | ||
|
||
assert_equal "test_42", streams.last |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dhh should people have access to the streams
here? Or is it better that they test what get's streamed?
|
||
identifiers.each do |identifier, val| | ||
define_singleton_method(identifier) { val } | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it's overly reaching into how identifiers work. (And doing it improperly since it seems to use attr_accessor
not singleton methods).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should care here about how identifiers really work. We only want to know that identifier has a value and can be accessed by its name. This is exactly what a channel knows about the connection identifiers.
And this makes our ConnectionStub
more abstract. It's just quacking like a connection.
16120f7
to
9ff9f06
Compare
@vipulnsward @kaspth Thanks for the reviews! I've updated the PR. |
It'd be awesome to have this implemented. Has there been any progress? |
9ff9f06
to
9e40836
Compare
Closed in favour of #33659 |
ActionCable::Channel::TestCase provides an ability to unit-test channel classes. There are several reasons to write unit/functional cable tests: - Access control (who has access to the channel? who can perform action and with which argument? - Frontend-less applications have no system tests at all–and we still need a way to test channels logic. See also rails#27191
ActionCable::Channel::TestCase provides an ability to unit-test channel classes. There are several reasons to write unit/functional cable tests: - Access control (who has access to the channel? who can perform action and with which argument? - Frontend-less applications have no system tests at all–and we still need a way to test channels logic. See also #27191
Extracted from #23211.
Example: