#ruby #rspec
Вопрос:
Все примеры модульных тестов должны быть высмеяны/заглушены. Rspec для метода проверки ошибок и метода результата.
require 'yaml'
require_relative 'checkerror'
class Operations
def initialize
@path
@checks_to_run
@check
end
# This will prints the result of each file offences or no offences
def result (result_log: File.new('result.txt', 'a '))
# @check.errors should be stubbed with a value to enter in if or else block
if @check.errors.empty?
# Output is printing in both console as well as file
result_log.write("#{@check.checker.file_path} :: No offensenses detectedn")
puts "#{@check.checker.file_path} :: No offensenses detectedn"
else
@check.errors.uniq.each do |err|
puts "#{@check.checker.file_path} : #{err}n"
result_log.write("#{@check.checker.file_path} : #{err}n")
end
end
result_log.close
end
def rules_to_run
@checks_to_run = YAML.load(File.read('lib/property.yaml'))
end
def path_of_directory
@path = gets.chomp
end
def checkerror
Dir[File.join(@path, '**/*.rb')].each do |file|
@check = CheckError.new(file)
@check.check_alphabetized_constants if @checks_to_run.include?('check_alphabetized_constants')
@check.check_empty_line_before_return if @checks_to_run.include?('check_empty_line_before_return')
#result is called to print errors
result
end
end
end
Я написал rspec для метода проверки ошибок
context '#checkerror' do
it 'only check the method is called or not' do
allow(@check_to_run).to receive(:include?).and_return(true)
allow(@check).to receive(:check_alphabetized_contants)
operation = Operation.new
operation.checkerror
expect(@check).to have_received(:check_alphabetized_constants)
end
end
но получаю ошибку
TypeError: no implicit conversion of nil into string.
Я думаю, что я не вошел в цикл конечного блока, и заглушка также синтаксически неверна, я думаю.
Комментарии:
1. Пожалуйста, отредактируйте свой вопрос, чтобы добавить полную трассировку стека и любые инициализаторы rspec, которые позволили бы воспроизвести вашу ошибку.
2. Это довольно неприятный вопрос, потому что вы просите о довольно конкретной помощи в реализации rspec, когда базовый код на самом деле не имеет смысла. Как я уже упоминал в предыдущем случае, когда вы опубликовали это, вы
@path
@check_to_run
на самом деле ни для чего не устанавливаете переменные или, поэтому этот код на самом деле недействителен; что вы ожидаетеDir[File.join(nil, '**/*.rb')].each ....
сделать?!3. Другими словами, вы не можете этого сделать:
operation = Operation.new
. ВашOperation
класс, по крайней мере, ожидает, что он будет инициализирован сpath
помощью «иchecks_to_run
«. Я попытаюсь переписать это для вас в ответе, но вам действительно нужно сделать шаг назад и написать правильную реализацию, прежде чем приступать к ее модульному тестированию.4. Конечно. Я сделаю
Ответ №1:
Вот довольно минимальное изменение в вашем коде, чтобы оно имело немного больше смысла и действительно могло быть протестировано:
require 'yaml'
require_relative 'checkerror'
class Operations
def initialize(path: path_of_directory, checks_to_run: rules_to_run):
@path = path
@checks_to_run = checks_to_run
end
def log_result(file, errors, result_log: File.new('result.txt', 'a '))
if errors.empty?
# Output is printing in both console as well as file
result_log.write("#{file} :: No offensenses detectedn")
puts "#{file} :: No offensenses detectedn"
else
errors.uniq.each do |err|
puts "#{file} : #{err}n"
result_log.write("#{file} : #{err}n")
end
end
result_log.close
end
def checkerror
Dir[File.join(@path, '**/*.rb')].each do |file|
checker = CheckError.new(file)
checker.check_alphabetized_constants if @checks_to_run.include?('check_alphabetized_constants')
checker.check_empty_line_before_return if @checks_to_run.include?('check_empty_line_before_return')
log_result(file, checker.errors)
end
end
# These methods feel very out-of-place!! It would be advisiable to move these outside of this class...
def rules_to_run
@checks_to_run = YAML.load(File.read('lib/property.yaml'))
end
def path_of_directory
@path = gets.chomp
end
end
Основные примечания:
- Вам нужно фактически задать переменные экземпляра. В вашей предыдущей реализации, например,
@path
былоnil
— так что это не имело никакого смысла. - Заглушать переменные типа
@checks_to_run
(которые вы, кстати, неправильно написали!) -не лучшая идея. Почему бы просто не установить переменную на желаемое значение, в первую очередь? - Вы не можете заглушать переменные, которые даже не инициализированы, например
@check
… так не работает заглушка. Заглушка не похожа на «жуткое действие на расстоянии», изменяющее то, как на самом деле ведет себя код; речь идет о настройке поведения зависимостей в ответ.
Итак, с учетом сказанного… Вот примерно как теперь может выглядеть ваш тест:
describe '#checkerror' do
let(:path) { Dir.mktmpdir('my-temporary-directory') }
let!(:rb_file_in_path) { Tempfile.new('test-file.rb', tmpdir) }
let(:mock_checker) { instance_double(CheckError) }
it 'calls check_alphabetized_contants when this is a check to run' do
allow(CheckError).to receive(:new).and_return(mock_checker)
allow(mock_checker).to receive(:check_alphabetized_constants)
operation = Operation.new(path: path, checks_to_run: ['check_alphabetized_constants'])
operation.checkerror
expect(mock_checker).to have_received(:check_alphabetized_constants)
end
end