Rails应用功能增强与测试实践
1. 投票回调功能实现
在开发过程中,我们希望仅为新提交的故事创建投票,而不是在每次更新现有故事时都创建投票。因此,我们使用
after_create
回调,而不是
after_save
回调。以下是回调方法的定义:
class Story < ApplicationRecord
after_create :create_initial_vote
# 其他模型代码...
protected
def create_initial_vote
votes.create user: user
end
end
这里,
votes.create user: user
这行代码直接使用了故事的
votes
关联和
user
属性来创建初始投票。
2. 为故事添加描述
为了让用户能对提交的故事进行更详细的描述,我们需要为
Story
模型添加一个
description
列。具体操作步骤如下:
1.
生成迁移文件
:
$ rails generate migration AddDescriptionToStories description:text
迁移文件内容如下:
class AddDescriptionToStories < ActiveRecord::Migration[5.0]
def change
add_column :stories, :description, :text
end
end
这里选择
text
类型是因为
string
类型最多只能存储255个字符,而故事描述可能会超过这个限制。
2.
应用迁移
:
$ rails db:migrate
3. 扩展提交表单
在测试初始投票创建代码之前,我们需要在故事提交表单中添加一个接受
description
列的字段。在
app/views/stories/new.html.erb
文件中添加以下代码:
<div>
<p><%= f.label :description %></p>
<%= f.text_area :description %>
</div>
同时,在
app/views/stories/show.html.erb
文件中显示故事描述:
<p>
<%= @story.description %>
</p>
4. 白名单新属性
为了确保安全,我们需要将新的
description
属性添加到
StoriesController
的允许属性列表中。修改
story_params
方法如下:
def story_params
params.require(:story).permit(:name, :link, :description)
end
5. 添加用户页面
为了按用户跟踪网站的使用历史,我们要创建一个用户页面,显示该用户最近提交的6个故事和最近投票的6个故事。
1.
引入连接模型关系
:
在
app/models/user.rb
文件中添加以下代码:
class User < ApplicationRecord
has_secure_password
has_many :stories
has_many :votes
has_many :stories_voted_on,
through: :votes,
source: :story
end
这里的
through: :votes
参数定义了这是一个连接模型关系,即“一个用户通过
Vote
模型拥有多个
Story
”。
2.
生成控制器
:
$ rails generate controller Users show
-
添加路由配置
:
在config/routes.rb文件中添加:
resources :users
-
实现
show动作 :
在app/controllers/user_controller.rb文件中:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@stories_submitted = @user.stories.
limit(6).order("stories.id DESC")
@stories_voted_on = @user.stories_voted_on.
limit(6).order("votes.id DESC")
end
end
-
创建视图模板
:
在app/views/users/show.html.erb文件中:
<h2>Stories submitted by <%= @user.name %></h2>
<div id="stories_submitted">
<%= render partial: @stories_submitted %>
</div>
<h2>Stories voted for by <%= @user.name %></h2>
<div id="stories_voted_on">
<%= render partial: @stories_voted_on %>
</div>
-
添加链接和样式
:
在app/views/stories/show.html.erb文件中添加用户页面链接:
<p class="submitted_by">
Submitted by:
<span><%= link_to @story.user.name, @story.user %></span>
</p>
在
app/asets/stylesheets/stories.scss
文件中添加样式:
.story p {
color: #666;
font-size: 0.8em;
}
h2 {
clear: both;
margin: 0;
padding: 10px 0;
}
-
重写
to_param方法 :
在app/models/user.rb文件中:
class User < ApplicationRecord
# 其他模型代码...
def to_param
"#{id}-#{name}"
end
end
6. 测试新功能
为了确保我们所做的更改能够正常工作,我们需要编写单元测试和功能测试。
1.
测试计数器缓存
:
在
test/models/story_test.rb
文件中:
class StoryTest < ActiveSupport::TestCase
# 其他测试方法...
test "increments vote counter cache" do
stories(:two).votes.create(user: users(:glenn))
stories(:two).reload
assert_equal 1, stories(:two).attributes['votes_count']
end
test "decrements votes counter cache" do
stories(:one).votes.first.destroy
stories(:one).reload
assert_equal 1, stories(:one).attributes['votes_count']
end
end
这里使用
reload
方法是因为使用计数器缓存和固定装置时,固定装置的计数器缓存属性在首次获取时可能不正确。
2.
测试初始投票创建
:
class StoryTest < ActiveSupport::TestCase
# 其他测试方法...
test "casts vote after creating story" do
s = Story.create(
name: "Vote SmartThe 2008 Elections",
link: "http://votesmart.org/",
user: users(:glenn)
)
assert_equal users(:glenn), s.votes.first.user
end
end
-
测试连接模型关系
:
在test/models/user_test.rb文件中扩展测试用例,以测试新添加的has_many :through关联。
通过以上步骤,我们完成了Rails应用的功能增强,并通过测试确保了新功能的正确性。整个过程的流程图如下:
graph TD;
A[实现投票回调] --> B[添加故事描述];
B --> C[扩展提交表单];
C --> D[白名单新属性];
D --> E[添加用户页面];
E --> F[测试新功能];
在这个过程中,我们逐步为应用添加了新的功能,并通过测试保证了功能的稳定性和正确性。每个步骤都有明确的代码实现和操作说明,希望能帮助你更好地理解和应用这些技术。
Rails应用功能增强与测试实践
7. 测试详细解析
在前面我们简单提及了对新功能的测试,接下来详细解析各个测试用例。
7.1 测试计数器缓存
-
增量测试
:
在test/models/story_test.rb文件中的test "increments vote counter cache"测试用例,具体代码如下:
class StoryTest < ActiveSupport::TestCase
# 其他测试方法...
test "increments vote counter cache" do
stories(:two).votes.create(user: users(:glenn))
stories(:two).reload
assert_equal 1, stories(:two).attributes['votes_count']
end
end
此测试用例的目的是验证当添加新投票时,缓存的投票计数是否正确增加。首先,我们为
stories(:two)
创建一个新的投票,使用
stories(:two).votes.create(user: users(:glenn))
。由于使用计数器缓存和固定装置时,固定装置的计数器缓存属性在首次获取时可能不正确,所以需要使用
stories(:two).reload
方法从数据库中重新加载模型,以确保获取到最新的缓存值。最后,使用
assert_equal 1, stories(:two).attributes['votes_count']
断言缓存的投票计数是否正确增加到1。
-
减量测试
:
class StoryTest < ActiveSupport::TestCase
# 其他测试方法...
test "decrements votes counter cache" do
stories(:one).votes.first.destroy
stories(:one).reload
assert_equal 1, stories(:one).attributes['votes_count']
end
end
这个测试用例与增量测试相反,用于验证当删除投票时,缓存的投票计数是否正确减少。首先,使用
stories(:one).votes.first.destroy
删除
stories(:one)
的第一个投票,然后使用
stories(:one).reload
重新加载模型,最后断言缓存的投票计数是否正确减少到1。
7.2 测试初始投票创建
class StoryTest < ActiveSupport::TestCase
# 其他测试方法...
test "casts vote after creating story" do
s = Story.create(
name: "Vote SmartThe 2008 Elections",
link: "http://votesmart.org/",
user: users(:glenn)
)
assert_equal users(:glenn), s.votes.first.user
end
end
该测试用例用于验证在创建故事后是否自动创建了初始投票。通过
Story.create
方法创建一个新的故事,并传入用户信息。然后使用
assert_equal users(:glenn), s.votes.first.user
断言新创建故事的第一个投票的用户是否与创建故事时传入的用户一致,以此来确保初始投票创建功能正常。
7.3 测试连接模型关系
在
test/models/user_test.rb
文件中扩展测试用例,以测试新添加的
has_many :through
关联。虽然文档中未给出具体测试代码,但我们可以推测测试的大致思路。例如,我们可以测试通过
User
对象的
stories_voted_on
方法是否能正确获取到用户投票的故事。以下是一个可能的测试示例:
class UserTest < ActiveSupport::TestCase
# 其他测试方法...
test "has many stories voted on through votes" do
user = User.first
voted_stories = user.stories_voted_on
assert_not_empty voted_stories
end
end
这个测试用例通过获取第一个用户的
stories_voted_on
关联集合,并使用
assert_not_empty
断言该集合不为空,以此来验证
has_many :through
关联是否正常工作。
8. 总结与注意事项
通过以上一系列的操作,我们成功地为Rails应用添加了新功能,包括投票回调、故事描述、用户页面等,并通过编写测试用例确保了这些功能的正确性。在整个过程中,有以下几点需要注意:
-
数据库迁移
:在添加新的模型属性时,要记得生成迁移文件并应用迁移,确保数据库结构与模型定义一致。
-
回调使用
:选择合适的回调方法,如
after_create
和
after_save
,根据具体需求来决定何时触发相应的操作。
-
测试覆盖
:编写全面的测试用例,覆盖模型的各种变化和新添加的关联关系,确保应用的稳定性和可靠性。
-
安全问题
:使用白名单机制来保护应用免受大规模赋值攻击,确保只允许合法的属性被赋值。
9. 相关操作总结表格
| 操作内容 | 具体步骤 | 涉及文件 |
|---|---|---|
| 实现投票回调 |
1. 在
Story
模型中定义
after_create :create_initial_vote
;2. 实现
create_initial_vote
方法
|
app/models/story.rb
|
| 添加故事描述 |
1. 生成迁移文件
rails generate migration AddDescriptionToStories description:text
;2. 应用迁移
rails db:migrate
|
db/migrate/xxx_add_description_to_stories.rb
|
| 扩展提交表单 |
1. 在
app/views/stories/new.html.erb
中添加描述字段;2. 在
app/views/stories/show.html.erb
中显示描述
|
app/views/stories/new.html.erb
,
app/views/stories/show.html.erb
|
| 白名单新属性 |
修改
stories_controller.rb
中的
story_params
方法
|
app/controllers/stories_controller.rb
|
| 添加用户页面 |
1. 引入连接模型关系;2. 生成控制器;3. 添加路由配置;4. 实现
show
动作;5. 创建视图模板;6. 添加链接和样式;7. 重写
to_param
方法
|
app/models/user.rb
,
app/controllers/user_controller.rb
,
config/routes.rb
,
app/views/users/show.html.erb
等
|
| 测试新功能 | 1. 测试计数器缓存;2. 测试初始投票创建;3. 测试连接模型关系 |
test/models/story_test.rb
,
test/models/user_test.rb
|
10. 未来扩展方向
基于当前的功能,我们可以考虑以下几个扩展方向:
-
更多用户交互
:例如允许用户编辑和删除自己提交的故事和投票。
-
数据统计
:对用户的提交和投票数据进行统计分析,如用户的活跃度、热门故事等。
-
社交功能
:添加用户之间的关注、分享等社交功能,增强用户之间的互动。
通过不断地扩展和优化,我们可以让这个Rails应用更加完善和强大。整个功能增强和测试的过程可以用以下流程图进一步概括:
graph LR;
subgraph 功能增强
A[投票回调] --> B[故事描述];
B --> C[提交表单扩展];
C --> D[属性白名单];
D --> E[用户页面添加];
end
subgraph 测试
F[计数器缓存测试] --> G[初始投票创建测试];
G --> H[连接模型关系测试];
end
E --> F;
通过以上的详细操作和测试,我们不仅为Rails应用添加了丰富的功能,还确保了这些功能的正确性和稳定性。同时,我们也为应用的未来扩展提供了一些思路,希望这些内容能对你的开发工作有所帮助。
超级会员免费看
10

被折叠的 条评论
为什么被折叠?



