如何用RSpec跟踪控制器方法流程?ROR测试@org_data未定义报错求助
Hey there! Let's break down what's going wrong with your test and controller code, and fix it step by step.
First: Why @org_data is undefined in your test
Your test has a couple of key issues that are preventing it from accessing the controller's instance variables:
- You're manually instantiating the controller with
@obj = DashboardsController.new, but when you runget :github_leader_board, RSpec Rails creates its own instance of the controller behind the scenes. Your@objhas nothing to do with the controller instance that actually runs the action. - Even if the controller ran correctly, instance variables from the controller (like
@org_data) don't automatically spill into your test's context. You need to use RSpec'sassignsmethod to access them.
Fixed Test Code
Here's how to rewrite your test to work properly with RSpec Rails' controller testing conventions:
describe DashboardsController do describe "#github_leader_board" do context "with valid params" do before do # Stub your API calls here to avoid hitting real external APIs in tests # Example stubs (adjust based on your actual API response structure): # allow(subject).to receive(:get_org_api).and_return([{name: "test"}]) # allow(subject).to receive(:get_team_api).with("test").and_return([{name: "team", id: 123}]) # allow(subject).to receive(:get_members).with(123).and_return([{id: "user1"}, {id: "user2"}]) # Add stubs for other API methods as needed end it "returns http success" do get :github_leader_board, params: { orgname: "test", teamname: "team" } expect(response).to have_http_status(:success) end it "assigns org_data and calls team API" do get :github_leader_board, params: { orgname: "test", teamname: "team" } # Access controller instance variables with assigns(:variable_name) expect(assigns(:org_data)).not_to be_nil expect(subject).to have_received(:get_team_api).with("test") end end end end
Important notes for the test:
- Stub external API calls: You never want tests hitting real external APIs (like GitHub) because it makes tests slow and flaky. Use RSpec's
allowto stub methods likeget_org_api,get_team_api, etc., to return fake data. - Use
assigns: This is how you access controller instance variables in RSpec controller tests. - Don't manually instantiate the controller: RSpec handles creating the controller instance when you run
get,post, etc.
Next: Fixes for Your Controller Code
Looking at your github_leader_board method, there's a critical bug that's also causing issues:
You wrote @orgs = get_names(org_data) but org_data isn't defined—you meant @org_data (the instance variable you just assigned). That's going to throw a NameError even before your test gets to check @org_data.
Here's a cleaned-up, fixed version of the controller method with optimizations:
def github_leader_board @org_data = get_org_api @orgs = get_names(@org_data) # Fixed: use @org_data instead of undefined org_data # Get team data only if orgname is present if params[:orgname].present? team_data = get_team_api(params[:orgname]) @teams = get_names(team_data) # Simplify team name to ID mapping (no need for multiple loops) team_id_map = team_data.each_with_object({}) do |team, hash| hash[team[:name]] = team[:id] # Adjust based on your actual API response structure end if params[:teamname].present? && team_id_map.key?(params[:teamname]) member_data = get_members(team_id_map[params[:teamname]]) @memberids = get_names(member_data) member_names = get_member_names(@memberids) # Calculate points more concisely review_comments = get_reviewcoments(@memberids) reactions = points(@memberids) @points_hash = member_names.zip(review_comments.zip(reactions).map { |a, b| a + b }).to_h end end end
Key improvements:
- Fixed the
org_data→@org_datatypo that would crash the controller. - Added guard clauses (
if params[:orgname].present?) to avoid running code when required params are missing. - Simplified the team name-to-ID mapping using
each_with_objectinstead of multiple loops—this is cleaner and more efficient. - Streamlined the points calculation using
zipinstead of transpose + reduce, which is more readable. - Added a check for
params[:teamname]existing in the team ID map to avoid unnecessary API calls.
Additional Best Practices
- Add error handling: What if
get_org_apireturns nil or an empty array? Add checks to handle those cases (e.g., redirect to an error page or show a flash message). - Extract logic to services: Your controller is doing a lot of work (API calls, data transformation). Move this logic into a service object (like
GitHubLeaderboardService) to keep your controller thin and make the logic easier to test in isolation. - Validate params: Even though you're using GET params, consider adding checks to ensure
orgnameandteamnameare valid (e.g., non-empty strings). - Test edge cases: Write tests for when
orgnameis missing,teamnamedoesn't exist for the org, API calls fail, etc.
内容的提问来源于stack exchange,提问作者Ashwini Ramesh

