У меня есть работник Sidekiq, который обращается к внешнему API, чтобы вернуть некоторые данные. Я пытаюсь написать тесты, чтобы убедиться, что этот рабочий спроектирован и работает правильно. Рабочий захватывает экземпляр локальной модели и исследует два поля модели. Если одно из полей - nil
, оно отправит другое поле в удаленный API.
Вот рабочий код:
class TokenizeAndVectorizeWorker
include Sidekiq::Worker
sidekiq_options queue: 'tokenizer_vectorizer', retry: true, backtrace: true
def perform(article_id)
article = Article.find(article_id)
tokenizer_url = ENV['TOKENIZER_URL']
if article.content.nil?
send_content = article.abstract
else
send_content = article.content
end
# configure Faraday
conn = Faraday.new(tokenizer_url) do |c|
c.use Faraday::Response::RaiseError
c.headers['Content-Type'] = 'application/x-www-form-urlencoded'
end
# get the response from the tokenizer
resp = conn.post '/tokenize', "content=#{URI.encode(send_content)}"
# the response's body contains the JSON for the tokenized and vectorized article content
article.token_vector = resp.body
article.save
end
end
Я хочу напишите тест, чтобы убедиться, что если содержание статьи равно нулю, то аннотацию статьи отправляют для кодирования.
Я предполагаю, что «правильный» способ сделать это - издеваться над ответами с помощью Фарадея, например что я ожидаю ответа c на конкретный c ввод. Создавая статью с содержанием nil
и рефератом x
, я могу имитировать ответ на отправку x
удаленному API и имитировать ответ на отправку nil
удаленному API. Я также могу создать статью с x
в качестве аннотации и z
в качестве содержания и фиктивных ответов для z
.
Я написал тест, который в основном издевается над Фарадеем:
it "should fetch the token vector on ingest" do
# don't wait for async sidekiq job
Sidekiq::Testing.inline!
# stub Faraday to return something without making a real request
allow_any_instance_of(Faraday::Connection).to receive(:post).and_return(
double('response', status: 200, body: "some data")
)
# create an attrs to hand to ingest
attrs = {
data_source: @data_source,
title: Faker::Book.title,
url: Faker::Internet.url,
content: Faker::Lorem.paragraphs(number: 5).join("<br>"),
abstract: Faker::Book.genre,
published_on: DateTime.now,
created_at: DateTime.now
}
# ingest an article from the attrs
status = Article.ingest(attrs)
# the ingest occurs roughly simultaneously to the submission to the
# worker so we need to re-fetch the article by the id because at that
# point it will have gotten the vector saved to the DB
@token_vector_article = Article.find(status[1].id)
# we should've saved "some data" as the token_vector
expect(@token_vector_article.token_vector).not_to eq(nil)
expect(@token_vector_article.token_vector).to eq("some data")
end
Но это высмеивает 100% использования Фарадея с помощью :post
. В моем конкретном случае я понятия не имею, как имитировать ответ :post
с определенным c телом ...
Также возможно, что я собираюсь тестировать все это неправильно. Я мог бы вместо этого проверять, что мы отправляем правильный контент (тест должен проверять, что отправляется с помощью Фарадея), и полностью игнорировать правильный ответ.
Каков правильный способ проверить, что этот рабочий делает правильное вещь (отправляет контент или отправляет аннотацию, если контент равен нулю)? Для проверки того, что отправляется, или для проверки того, что мы получаем обратно, как отражение отправляемого?
Если я должен проверять, что возвращается как отражение отправляемого, как мне высмеивать разные ответы от Фарадея в зависимости от ценности отправляемого ему сообщения /
** примечание добавлено позже **
Я еще немного покопался и подумал: «Хорошо, позвольте мне проверить, что я отправив запрос, который я ожидаю, и что я правильно обрабатываю ответ. Итак, я попытался использовать webmock.
it "should fetch token vector for article content when content is not nil" do
require 'webmock/rspec'
# don't wait for async sidekiq job
Sidekiq::Testing.inline!
request_url = "#{ENV['TOKENIZER_URL']}/tokenize"
# webmock the expected request and response
stub = stub_request(:post, request_url)
.with(body: 'content=y')
.to_return(body: 'y')
# create an attrs to hand to ingest
attrs = {
data_source: @data_source,
title: Faker::Book.title,
url: Faker::Internet.url,
content: "y",
abstract: Faker::Book.genre,
published_on: DateTime.now,
created_at: DateTime.now
}
# ingest an article from the attrs
status = Article.ingest(attrs)
# the ingest occurs roughly simultaneously to the submission to the
# worker so we need to re-fetch the article by the id because at that
# point it will have gotten the vector saved to the DB
@token_vector_article = Article.find(status[1].id)
# we should have sent a request with content=y
expect(stub).to have_been_requested
# we should've saved "y" as the token_vector
expect(@token_vector_article.token_vector).not_to eq(nil)
expect(@token_vector_article.token_vector).to eq("y")
end
Но я думаю, что webmock не обрабатывается внутри задания sidekiq, потому что я получаю следующее:
1) Article tokenization and vectorization should fetch token vector for article content when content is not nil
Failure/Error: expect(stub).to have_been_requested
The request POST https://zzzzz/tokenize with body "content=y" was expected to execute 1 time but it executed 0 times
The following requests were made:
No requests were made.
============================================================
Если я попробую чтобы включить webmock/rspec
в любом другом месте, например, в начале моего файла, случайные вещи начнут взрываться. Например, если у меня есть эти строки в начале этого файла spe c:
require 'spec_helper'
require 'rails_helper'
require 'sidekiq/testing'
require 'webmock/rspec'
Тогда я получаю:
root@c18df30d6d22:/usr/src/app# bundle exec rspec spec/models/article_spec.rb:174
database: test
Run options: include {:locations=>{"./spec/models/article_spec.rb"=>[174]}}
There was an error creating the elasticsearch index for Article: #<NameError: uninitialized constant Faraday::Error::ConnectionFailed>
There was an error removing the elasticsearch index for Article: #<NameError: uninitialized constant Faraday::Error::ConnectionFailed>
Что, как я предполагаю, связано с тем, что набор тестов пытается инициализировать материал, но мешает webmock ...