我对ruby比较新,从Python和C#这样的语言进入它,所以请原谅我,如果这是一个我错过的明显错误。
我正在编写一个工具代码来帮助自己和其他开发人员生成.editorconfig文件,以此来学习Ruby。我使用rspec作为我的测试框架并编写了以下测试:
module EditorConfigGenerator
RSpec.describe EditorConfigGenerator::FileGenerator do
configs_sensible_defaults = [EditorConfigGenerator::EditorConfig.new({root: true, indent_style: 'space', indent_size: 2,
end_of_line: 'lf', charset: 'utf-8',
trim_trailing_whitespace: true, insert_final_newline: true})]
file_generator = EditorConfigGenerator::FileGenerator.new(configs_sensible_defaults)
context "A collection of one EditorConfig object is provided" do
it "displays a preview of the file output" do
# Newlines are automatically inserted into ruby multi-line strings
output = "root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
"
# String.delete(' ') to remove any potential formatting discrepancies
# which don't affect the test of the content.
expect(file_generator.preview_output.delete(' ')).to eq output.delete(' ')
end
it "creates a .editorconfig file in the correct location" do
dot_editorconfig = file_generator.generate_config_file('test_output/.editorconfig')
expect(dot_editorconfig.class).to eq File
expect(File.exist? "test_output/.editorconfig").to eq true
end
end
context "A collection of multiple EditorConfig objects is provided" do
it "displays a preview of the file output" do
configs = configs_sensible_defaults.push(EditorConfigGenerator::EditorConfig.new({file_type: '*.{js,py}', indent_size: 4}))
file_generator = EditorConfigGenerator::FileGenerator.new(configs)
output = "root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,py}]
indent_size = 4"
expect(file_generator.preview_output.delete(' ')).to eq output.delete(' ')
end
end
end
end
当我最初几次进行这些测试时,他们看起来很好。然后,我在spec_helper.rb
文件
config.profile_examples = 10
config.order = :random
kernel.srand config.seed
我再次运行了几次测试,所有测试都经过 - 看似随机 - 我用种子31166
获得了以下失败输出:
1) EditorConfigGenerator::FileGenerator A collection of one EditorConfig object is provided displays a preview of the file output
Failure/Error: expect(file_generator.preview_output.delete(' ')).to eq output.delete(' ')
expected: "root=true\n[*]\nindent_style=space\nindent_size=2\nend_of_line=lf\ncharset=utf-8\ntrim_trailing_whitespace=true\ninsert_final_newline=true\n"
got: "root=true\n[*]\nindent_style=space\nindent_size=2\nend_of_line=lf\ncharset=utf-8\ntrim_trailing_whitespace=true\ninsert_final_newline=true\n[*.{js,py}]\nindent_size=4"
(compared using ==)
Diff:
@@ -6,4 +6,6 @@
charset=utf-8
trim_trailing_whitespace=true
insert_final_newline=true
+[*.{js,py}]
+indent_size=4
# ./spec/file_generator_spec.rb:23:in `block (3 levels) in <module:EditorConfigGenerator>'
我尝试了其他种子,但它有效,但种子31166
失败了。
输出让我觉得它是上下文的,所以我试图在我的实现中寻找一个bug。我没有找到一个,所以我认为这可能是我在规范中定义共享变量configs_sensible_defaults
的方式的问题。
我决定更改代码以使用before(:each)
在每次测试之前分配一个实例变量(即正确地重置数据),它似乎解决了它。
这里是固定的spec/file_generator_spec.rb
module EditorConfigGenerator
RSpec.describe EditorConfigGenerator::FileGenerator do
before(:each) do
@configs_sensible_defaults = [EditorConfigGenerator::EditorConfig.new({root: true, indent_style: 'space', indent_size: 2, end_of_line: 'lf', charset: 'utf-8', trim_trailing_whitespace: true, insert_final_newline: true})]
@file_generator = EditorConfigGenerator::FileGenerator.new(@configs_sensible_defaults)
end
context "A collection of one EditorConfig object is provided" do
it "displays a preview of the file output" do
# Newlines are automatically inserted into ruby multi-line strings
output = "root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
"
# String.delete(' ') to remove any potential formatting discrepancies
# which don't affect the test of the content.
expect(@file_generator.preview_output.delete(' ')).to eq output.delete(' ')
end
it "creates a .editorconfig file in the correct location" do
dot_editorconfig = @file_generator.generate_config_file('test_output/.editorconfig')
expect(dot_editorconfig.class).to eq File
expect(File.exist? "test_output/.editorconfig").to eq true
end
end
context "A collection of multiple EditorConfig objects is provided" do
it "displays a preview of the file output" do
configs = @configs_sensible_defaults.clone.push(EditorConfigGenerator::EditorConfig.new({file_type: '*.{js,py}', indent_size: 4}))
@file_generator = EditorConfigGenerator::FileGenerator.new(configs)
output = "root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,py}]
indent_size = 4"
expect(@file_generator.preview_output.delete(' ')).to eq output.delete(' ')
end
end
end
end
这是两个文件(view on Github)的差异输出:
RSpec.describe EditorConfigGenerator::FileGenerator do
configs_sensible_defaults = [EditorConfigGenerator::EditorConfig.new({root: true, indent_style: 'space', indent_size: 2,
end_of_line: 'lf', charset: 'utf-8',
trim_trailing_whitespace: true, insert_final_newline: true})]
file_generator = EditorConfigGenerator::FileGenerator.new(configs_sensible_defaults)
before(:each) do
@configs_sensible_defaults = [EditorConfigGenerator::EditorConfig.new({root: true, indent_style: 'space', indent_size: 2, end_of_line: 'lf', charset: 'utf-8', trim_trailing_whitespace: true, insert_final_newline: true})]
@file_generator = EditorConfigGenerator::FileGenerator.new(@configs_sensible_defaults)
end
context "A collection of one EditorConfig object is provided" do
it "displays a preview of the file output" do
@ file_generator_spec.rb:22 @ module EditorConfigGenerator
"
# String.delete(' ') to remove any potential formatting discrepancies
# which don't affect the test of the content.
expect(file_generator.preview_output.delete(' ')).to eq output.delete(' ')
expect(@file_generator.preview_output.delete(' ')).to eq output.delete(' ')
end
it "creates a .editorconfig file in the correct location" do
dot_editorconfig = file_generator.generate_config_file('test_output/.editorconfig')
dot_editorconfig = @file_generator.generate_config_file('test_output/.editorconfig')
expect(dot_editorconfig.class).to eq File
expect(File.exist? "test_output/.editorconfig").to eq true
end
end
context "A collection of multiple EditorConfig objects is provided" do
最后,对于上下文,以下是相关的file_generator.rb
和editor_config.rb
文件
module EditorConfigGenerator
class FileGenerator
def initialize(configs)
@configs = configs
end
def preview_output
output = ""
@configs.each do |config|
if output.include? "root="
output << config.to_s_without_root
next
end
output << config.to_s
end
return output.rstrip if @configs.size > 1
output
end
def generate_config_file(location='.editorconfig')
File.delete(location) if File.exist? location
file = File.new(location, "w")
file.print(preview_output)
file.close
file
end
end
end
module EditorConfigGenerator
class EditorConfig
attr_reader :root, :indent_style, :indent_size,
:end_of_line, :charset, :trim_trailing_whitespace,
:insert_final_newline, :file_type
def initialize(options)
@root = nil
@file_type = :*
transform_options(options)
set_options(options)
end
def set_options(options)
@root = options[:root] unless options[:root].nil?
@indent_style = options[:indent_style] unless options[:indent_style].nil?
@indent_size = options[:indent_size] unless options[:indent_size].nil?
@end_of_line = options[:end_of_line] unless options[:end_of_line].nil?
@charset = options[:charset] unless options[:charset].nil?
@trim_trailing_whitespace = options[:trim_trailing_whitespace] unless options[:trim_trailing_whitespace].nil?
@insert_final_newline = options[:insert_final_newline] unless options[:insert_final_newline].nil?
@file_type = options[:file_type] unless options[:file_type].nil?
end
def transform_options(options)
options[:root] = true if options[:root] == 'y'
options[:root] = false if options[:root] == 'n'
options[:trim_trailing_whitespace] = true if options[:trim_trailing_whitespace] == 'y'
options[:trim_trailing_whitespace] = false if options[:trim_trailing_whitespace] == 'n'
options[:insert_final_newline] = true if options[:insert_final_newline] == 'y'
options[:insert_final_newline] = false if options[:insert_final_newline] == 'n'
end
def to_s
config_string = ""
config_string << "root = #{@root.to_s}\n" unless @root.nil?
config_string << "[#{@file_type}]\n"
config_string << "indent_style = #{@indent_style}\n" unless @indent_style.nil?
config_string << "indent_size = #{@indent_size}\n" unless @indent_size.nil?
config_string << "end_of_line = #{@end_of_line}\n" unless @end_of_line.nil?
config_string << "charset = #{@charset}\n" unless @charset.nil?
config_string << "trim_trailing_whitespace = #{@trim_trailing_whitespace.to_s}\n" unless @trim_trailing_whitespace.nil?
config_string << "insert_final_newline = #{@insert_final_newline.to_s}\n" unless @insert_final_newline.nil?
config_string
end
def to_s_without_root
lines = to_s.lines
lines.delete_at 0
lines.join
end
def to_s_without_line(line_identifier)
lines = to_s.lines
index = lines.index(line_identifier)
lines.delete_at index
lines.join
end
end
end
有人可以解释为什么更改解决了这个问题? 更改是否解决了问题?
我认为,configs_sensible_defaults
中的集合被突变并且在每次运行后没有被正确重置,这可以解释为什么某个种子会触发失败(可能在种子测试下) 2将在1)之前运行。
答案 0 :(得分:2)
它有时而不是其他时间工作的原因是因为你的一个测试(在第二个上下文中)被重新分配file_generator
。第一个上下文中的测试是使用共享外部作用域中的file_generator
。只要该测试首先运行,它就按预期工作。然后,第二个范围中的第二个测试运行,重新分配file_generator
,并且也通过了。
由于测试以随机顺序运行,每当它们按照文件中的顺序运行时,一切都在通过。当第二个测试首先运行时,它覆盖file_generator
,第二个测试仍然通过,但第一个测试以覆盖的值运行。
您可以使用before
块为每个测试配置内容,但您应该只在前一个块中配置公共,共享值。对于每个上下文或每个测试,任何不同的东西都应该在接近使用它的范围内进行初始化。
在上面的示例中,我不会使用任何before
块,至少不在外部范围内。 @config_sensible_defaults
和@file_generator
在每个上下文中都不同,根本不应该共享。如果要在具有相同默认值和生成器的上下文中将一组测试组合在一起,可以将before
块放在 context
块中,以正确初始化事物对于每个背景。