RSpec Stub AWS SQS
Neste post montro como criar testes automáticos utilizando o RSpec, para o módulo SQS (Simple Queue Service) do AWS SDK v2. Confira Stub Aws SQS.
Decidi escrever o artigo RSpec Stub Aws SQS pois, a pouco tempo precisei criar testes para uma implementação que fiz utilizando o client SQS do Aws SDK v2, e não encontrei muita informação a respeito disso. Googlando, encontrei a documentação oficial da AWS que dá exemplos voltados para o Bucket S3, porém precisava criar stub para o SQS (Simple Queue Service). Continuei buscando e o que encontrei foram cópias da documentação oficial, logo não me ajudou.
Visando ajudar outros Devs, este post vai tratar da criação de testes automáticos para o módulo Aws::SQS do AWS SDK v2, utilizando o RSpec.
Lembrando que estamos utilizando o Ruby 2.1.8 e aws-sdk 2.9.7.
Montando o ambiente de teste
Crie um diretório para o nosso projeto teste:
mkdir rspec_aws_sdk_v2_sqs
Acesse o diretório criado e crie um Gemfile utilizando o bundle:
cd rspec_aws_sdk_v2_sqs
bundle --init
Adicione a gem do RSpec e do AWS SDK v2 no Gemfile:
source "https://rubygems.org"
gem 'aws-sdk', '~> 2'
gem 'rspec'
Caso queira debugar os teste, adicione também o byebug no Gemfile:
source "https://rubygems.org"
gem 'aws-sdk', '~> 2'
gem 'rspec'
gem 'byebug'
Após instale todas as gens utilizando o bundler:
bundle install
Agora crie a estrutura de diretório e arquivos padrão do RSpec:
rspec --init
Ao rodar o comando rspec –init, foram criados 2 arquivos e um diretório. No arquivo .rspec ficam as flags que são passadas para o RSpec quando a gente roda os testes. No diretório spec foi criado o arquivo spec_helper.rb, este arquivo contêm todas as configurações comuns entre os testes que criarmos. É dentro do diretório spec que devemos por os arquivos de teste.
Agora vamos criar o arquivo que irá conter nossos testes do módulo AWS::SQS.
touch spec/aws_sqs_spec.rb
Feito isso, podemos começar a criar os testes.
Habilitando o stub AWS SQS
Para permitir que o AWS SDK v2 aceite stubs, devemos utilizar a seguinte configuração:
Aws.config.update(stub_responses: true)
Ponha esta configuração antes do end, no final do arquivo spec_helper.rb. Não esqueça de fazer o require da gem da AWS.
require 'aws-sdk'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
Aws.config.update(stub_responses: true)
end
A partir daí, já temos o ambiente pronto para o stub AWS.
Repare que removi todos os comentários que existiam neste arquivo.
Escrevendo o esqueleto do arquivo de testes
Abra o arquivo aws_sqs_spec.rb e ponha os seguintes comandos:
require 'spec_helper.rb'
describe "AWS::SQS" do
let(:queue_url){"https://sqs.us-west-2.amazonaws.com/943154236803/gabrielzuqueto_eti_br"}
let(:queue_name){:gabrielzuqueto_eti_br}
before do
@client_aws_sqs = Aws::SQS::Client.new
end
end
Na primeira linha, a gente está carregando o arquivo de configuração do rspec no nosso teste.
require 'spec_helper.rb'
Após iniciamos o bloco geral dos nossos testes:
describe "AWS::SQS" do
end
Dentro do bloco, definimos 2 variáveis de instância, que vão nos ajudar a criar um padrão.
let(:queue_url){"https://sqs.us-west-2.amazonaws.com/943154236803/gabrielzuqueto_eti_br"}
let(:queue_name){:gabrielzuqueto_eti_br}
Daí, criamos um bloco que será executado, antes de cada teste posto dentro do bloco geral:
before do
@client_aws_sqs = Aws::SQS::Client.new
end
Adicionamos dentro do bloco before, uma variável global chamada client_aws_sqs que recebe uma instância do Aws::SQS::Client.
A partir desta variável global, faremos nossos testes.
Stub AWS SQS Client create_queue
O método create_queue da classe Aws::SQS::Client, é reponsável por criar a fila. Este método aceita vários argumentos, porém vou me ater à criação padrão.
Lembrando que validações de caracteres no nome da fila, tamanho do nome, entre outros, não são validados pelo SDK e sim pelo servidor SQS, logo não conseguiremos testar estes tipos de validações.
describe "When called create_queue" do
it "Should error if queue_name is invalid" do
expect { @client_aws_sqs.create_queue({queue_name: nil}) }.to raise_error(ArgumentError)
end
it "Should success" do
@client_aws_sqs.stub_responses(:create_queue, queue_url: queue_url)
response = @client_aws_sqs.create_queue({queue_name: queue_name})
expect ( response.successful? ).should be_truthy
expect ( response.queue_url ).should eq(queue_url)
end
end
No primeiro teste a gente garante que se for passado um valor nulo, um erro é retornado.
No segundo teste, a gente cria um stub para o Aws SQS Client create_queue, o qual retornará queue_url com o valor da nossa variável de instância, queue_url. Após a gente “cria” a fila e verifica se tudo o correu bem. E para finalizar, a gente confere se o valor de queue_url confere com o esperado.
Stub AWS SQS Client purge_queue
O método purge_queue da classe Aws::SQS::Client, é reponsável por limpar toda a fila sem excluí-la. Este método espera receber a URL da fila.
describe "When called purge_queue" do
it "Should return error if queue_url is invalid" do
expect { @client_aws_sqs.purge_queue({queue_url: nil}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.purge_queue({queue_url: ""}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.purge_queue({queue_url: "something"}) }.to raise_error(ArgumentError)
end
it "Should return error if queue doesn't exists" do
@client_aws_sqs.stub_responses(:purge_queue, 'NonExistentQueue')
expect { @client_aws_sqs.purge_queue({queue_url: queue_url}) }.to raise_error(Aws::SQS::Errors::NonExistentQueue)
end
it "Should success" do
response = @client_aws_sqs.purge_queue({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response ).should eq(Aws::EmptyStructure.new)
end
end
O primeiro teste garante que se queue_url não contiver uma URL válida, um erro será retornado.
O segundo teste garante que se tentar limpar uma fila inexistente, um erro será retornado. Neste caso a gente criou um stub para purgue_queue que irá retornar o erro NonExistentQueue. Por isso a genre verifica no final se realmente este erro foi retornado.
O terceiro garante que se uma fila for purgada, será retornado sucesso. Além disso, a gente garante que a estrutura retornada é do tipo Aws::EmptyStructure como indiado na documentação.
Stub AWS SQS Client delete_queue
O método delete_queue da classe Aws:SQS:Client é responsável por eliminar uma fila, independente do conteúdo dela. Este método, como a maioria dos outros, espera o parâmetro queue_url.
describe "When called delete_queue" do
it "Should return error if queue_url is invalid" do
expect { @client_aws_sqs.delete_queue({queue_url: nil}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_queue({queue_url: ""}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_queue({queue_url: "something"}) }.to raise_error(ArgumentError)
end
it "Should return error if queue doesn't exists" do
@client_aws_sqs.stub_responses(:delete_queue, 'NonExistentQueue')
expect { @client_aws_sqs.delete_queue({queue_url: queue_url}) }.to raise_error(Aws::SQS::Errors::NonExistentQueue)
end
it "Should success" do
response = @client_aws_sqs.delete_queue({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response ).should eq(Aws::EmptyStructure.new)
end
end
Os testes do método delete_queue são bem parecidos com os do método purge_queue, logo dispensa explicação.
Stub AWS SQS Client get_queue_attributes
O método get_queue_attributes da classe Aws:SQS:Client é responsável por retornar os atributos solicitados, de uma fila. Como de praxe ele espera receber o parâmetro queue_url, além da lista de atributos que deverá ser retornada.
Este método é muito útil para sabermos o tamanho aproximado da fila, além de saber quantas mensagens estão in flight (o mesmo que indisponível para ser recuperada. Ela existe e poderá voltar para a fila, porém está “oculta” no momento).
describe "When called get_queue_attributes" do
it "Should return error if queue_name is invalid" do
expect { @client_aws_sqs.delete_queue({queue_url: nil}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_queue({queue_url: ""}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_queue({queue_url: "something"}) }.to raise_error(ArgumentError)
end
describe "and expect returns successful" do
before do
@client_aws_sqs.stub_responses(:get_queue_attributes, attributes: {
"ApproximateNumberOfMessages" => "10",
"ApproximateNumberOfMessagesNotVisible" => "8",
"ApproximateNumberOfMessagesDelayed" => "0"
})
@response = @client_aws_sqs.get_queue_attributes({queue_url: queue_url, attribute_names: [
"ApproximateNumberOfMessages",
"ApproximateNumberOfMessagesNotVisible",
"ApproximateNumberOfMessagesDelayed"
]})
expect ( @response.successful? ).should be_truthy
end
it "Should return the requested attributes - model 1" do
expect ( @response.attributes.length ).should eq(3)
expect ( @response.attributes["ApproximateNumberOfMessages"] ).should eq("10")
expect ( @response.attributes["ApproximateNumberOfMessagesNotVisible"] ).should eq("8")
expect ( @response.attributes["ApproximateNumberOfMessagesDelayed"] ).should eq("0")
end
it "Should return the requested attributes - model 2" do
expected_response = Aws::SQS::Types::GetQueueAttributesResult.new(attributes: {
"ApproximateNumberOfMessages"=>"10",
"ApproximateNumberOfMessagesNotVisible"=>"8",
"ApproximateNumberOfMessagesDelayed"=>"0"
})
expect ( @response ).should eq(expected_response)
end
end
end
O primeiro teste é bem parecido com os outros… Mais uma vez estamos testando o parâmetro queue_url.
O segundo teste ficou mais complexo pois estamos utilizando duas formas de testar a mesma coisa. Vamos aos detalhes…
Primeiro criamos um novo escopo de testes dentro de um outro:
describe "and expect returns successful" do
end
A partir daí, pudemos criar um bloco que irá executar apenas quando os testes deste novo escopo forem executados:
before do
@client_aws_sqs.stub_responses(:get_queue_attributes, attributes: {
"ApproximateNumberOfMessages" => "10",
"ApproximateNumberOfMessagesNotVisible" => "8",
"ApproximateNumberOfMessagesDelayed" => "0"
})
@response = @client_aws_sqs.get_queue_attributes({queue_url: queue_url, attribute_names: [
"ApproximateNumberOfMessages",
"ApproximateNumberOfMessagesNotVisible",
"ApproximateNumberOfMessagesDelayed"
]})
expect ( @response.successful? ).should be_truthy
end
Este bloco cria o stub para o get_queue_attributes:
@client_aws_sqs.stub_responses(:get_queue_attributes, attributes: {
"ApproximateNumberOfMessages" => "10",
"ApproximateNumberOfMessagesNotVisible" => "8",
"ApproximateNumberOfMessagesDelayed" => "0"
})
Após faz a solicitação dos atributos pelo Aws::SQS::Client e atribui o retorno a uma variável global, para que podemos testá-la nos próximos testes deste escopo:
@response = @client_aws_sqs.get_queue_attributes({queue_url: queue_url, attribute_names: [
"ApproximateNumberOfMessages",
"ApproximateNumberOfMessagesNotVisible",
"ApproximateNumberOfMessagesDelayed"
]})
Por fim, já faz a primeira verificação de sucesso:
expect ( @response.successful? ).should be_truthy
No mesmo escopo “and expect returns successful”, criamos 2 modelos de teste que fazem a mesma coisa, sendo que um pode conter mais atributos, pois testamos apenas alguns:
it "Should return the requested attributes - model 1" do
expect ( @response.attributes.length ).should eq(3)
expect ( @response.attributes["ApproximateNumberOfMessages"] ).should eq("10")
expect ( @response.attributes["ApproximateNumberOfMessagesNotVisible"] ).should eq("8")
expect ( @response.attributes["ApproximateNumberOfMessagesDelayed"] ).should eq("0")
end
E o outro deve ser exatamente igual ao esperado:
it "Should return the requested attributes - model 2" do
expected_response = Aws::SQS::Types::GetQueueAttributesResult.new(attributes: {
"ApproximateNumberOfMessages"=>"10",
"ApproximateNumberOfMessagesNotVisible"=>"8",
"ApproximateNumberOfMessagesDelayed"=>"0"
})
expect ( @response ).should eq(expected_response)
end
Vale pensar bem antes de escolher o segundo modelo, pois é muito específico.
Stub AWS SQS Client get_queue_url
Todo o módulo Aws::SQS é baseado na URL da fila, logo faremos 2 testes cobrindo o método get_queue_url:
describe "When called get_queue_url" do
it "Should return NonExistentQueue error" do
@client_aws_sqs.stub_responses(:get_queue_url, 'NonExistentQueue')
expect { @client_aws_sqs.get_queue_url({queue_name: queue_name}) }.to raise_error(Aws::SQS::Errors::NonExistentQueue)
end
it "Should return queue URL" do
@client_aws_sqs.stub_responses(:get_queue_url, queue_url: queue_url)
expect ( @client_aws_sqs.get_queue_url({queue_name: queue_name}).queue_url ).should eq(queue_url)
end
end
O primeiro item do teste de get_queue_url,testa se retorna erro caso a fila não exista.
it "Should return NonExistentQueue error" do
@client_aws_sqs.stub_responses(:get_queue_url, 'NonExistentQueue')
expect { @client_aws_sqs.get_queue_url({queue_name: queue_name}) }.to raise_error(Aws::SQS::Errors::NonExistentQueue)
end
O que fizemos foi dizer ao SQS que ao requisitar a URL da fila pelo método get_queue_url, deve retornar o erro de fila inexistente. Após a gente verifica com o expect, se realmente retornou o erro esperado.
Pode parecer sem sentido por estarmos testando puramente o SDK da Amazon, porém ao abstrair o módulo AWS::SQS e seus métodos através de uma classe, faz sentido testar o fluxo de quando uma fila não existe.
Note que estamos utilizando nossa variávelde instância: {queue_name: queue_name}
O segundo teste, testa se realmente a URL da fila foi retornada e se é a que esperamos.
it "Should return queue URL" do
@client_aws_sqs.stub_responses(:get_queue_url, queue_url: queue_url)
expect ( @client_aws_sqs.get_queue_url({queue_name: queue_name}).queue_url ).should eq(queue_url)
end
Neste caso, pedimos ao SQS que retorne a propriedade queue_url, que terá o valor da variável de instância queue_url. Após a gente verifica se realmente o valor da propriedade queue_url é o valor que a gente acabou de fazer stub.
Stub Aws SQS Client send_message
O método send_message espera no mínimo 2 parâmetros: queue_url e message_body. O primeiro parâmetro deve ser uma URL válida e o segundo não pode ser nulo. Logo faremos 3 testes para cobrir este método.
describe "When called send_message" do
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.send_message({queue_url: "", message_body: "something" }) }.to raise_error(ArgumentError)
end
it "Should return ArgumentError if message_body is nil" do
expect { @client_aws_sqs.send_message({queue_url: queue_url, message_body: nil }) }.to raise_error(ArgumentError)
end
it "Should return success" do
expect ( @client_aws_sqs.send_message({queue_url: queue_url, message_body: "something" }).successful? ).should be_truthy
end
end
No primeiro teste a gente envia uma URL inválida e espera que dê erro de argumento inválido:
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.send_message({queue_url: "", message_body: "something" }) }.to raise_error(ArgumentError)
end
No segundo teste,a gente envia uma mensagem inválida e espera que dê também o erro de argumento inválido:
it "Should return ArgumentError if message_body is nil" do
expect { @client_aws_sqs.send_message({queue_url: queue_url, message_body: nil }) }.to raise_error(ArgumentError)
end
No terceiro teste, a gente envia uma mensagem para a fila e espera que seja entregue com sucesso:
it "Should return success" do
expect ( @client_aws_sqs.send_message({queue_url: queue_url, message_body: "something" }).successful? ).should be_truthy
end
Stub Aws SQS Client receive_message
O método receive_message da classe Aws::SQS::Client é responsável por recuperar as mensagens da fila. Ele possui argumentos, porém vamos testar apenas o parâmetro max_number_of_messages, que por default é 1, ou seja, recupera apenas uma mensagem por vez, porém faremos um teste aumentando o valor deste parâmetro. Lembrando que o parâmetro queue_url é obrigatório.
describe "When called receive_message" do
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.receive_message({queue_url: ""}) }.to raise_error(ArgumentError)
end
it "Should return one message" do
@client_aws_sqs.stub_responses(:receive_message, messages: [body: "something"])
response = @client_aws_sqs.receive_message({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(1)
expect ( response.messages.first.class).should eq(Aws::SQS::Types::Message)
expect ( response.messages.first.body).should eq("something")
end
it "Should return two messages" do
@client_aws_sqs.stub_responses(:receive_message, messages: [{body: "something"}, {body: "anything"}])
response = @client_aws_sqs.receive_message({queue_url: queue_url, max_number_of_messages: 10})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(2)
expect ( response.messages.first.body).should eq("something")
expect ( response.messages.last.body).should eq("anything")
end
it "Should return no messages" do
@client_aws_sqs.stub_responses(:receive_message, messages: [])
response = @client_aws_sqs.receive_message({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(0)
end
end
O primeiro teste a gente já sabe que é o teste de URL inválida:
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.receive_message({queue_url: ""}) }.to raise_error(ArgumentError)
end
O segundo teste a gente está testando o recebimento de uma mensagem. Para que o teste passe, as 4 condições devem ser satisfeitas, caso contrário o teste irá quebra:
it "Should return one message" do
@client_aws_sqs.stub_responses(:receive_message, messages: [body: "something"])
response = @client_aws_sqs.receive_message({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(1)
expect ( response.messages.first.class).should eq(Aws::SQS::Types::Message)
expect ( response.messages.first.body).should eq("something")
end
A primeira condição verifica se recebemos a mensagem com sucesso.
A segunda condição verifica se recebemos apenas uma mensagem como esperávamos.
A terceira condição verifica se a classe da mensagem é a esperada (Aws::SQS::Types::Message).
A quarta condição verifica se o conteúdo da mensagem recebida é o esperado (something)
No terceiro teste, a gente verifica o recbimento de mais de uma mensagem. Para simular o real cenário, alteramos o parâmetro max_number_of_messages para 10 (máximo permitido pela AWS).
it "Should return two messages" do
@client_aws_sqs.stub_responses(:receive_message, messages: [{body: "something"}, {body: "anything"}])
response = @client_aws_sqs.receive_message({queue_url: queue_url, max_number_of_messages: 10})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(2)
expect ( response.messages.first.body).should eq("something")
expect ( response.messages.last.body).should eq("anything")
end
Fizemos os mesmos testes do teste anterior, porém sem o teste da classe das mensagens e com o teste do conteúdo das duas mensagens.
Lembrando que no caso do stub de messages, poderíamos criar stub para os demais parâmetros como: message_id, receipt_handle, attributes, message_attributes…
Exemplo de Stub Aws SQS Client receive_message com message_id e receipt_hanlde:
# Stub receive_message com uma mensagem
@client_aws_sqs.stub_responses(:receive_message, messages: [
message_id: "Something",
receipt_handle: "Something",
body: "Something"
])
# Stub receive_message com mais de uma mensagem
@client_aws_sqs.stub_responses(:receive_message, messages: [
{
message_id: "Something",
receipt_handle: "Something",
body: "Something"
},
{
message_id: "Something_2",
receipt_handle: "Something_2",
body: "Something_2"
}
])
No quarto teste a gente verifica o que acontece caso nenhuma mesagem seja recuperada, ou seja, caso a fila esteja vazia.
it "Should return no messages" do
@client_aws_sqs.stub_responses(:receive_message, messages: [])
response = @client_aws_sqs.receive_message({queue_url: queue_url})
expect ( response.successful? ).should be_truthy
expect ( response.messages.length ).should eq(0)
end
O retorno deve ser com sucesso porém sem nenhuma mensagem.
Stub Aws SQS Client delete_message
O método delete_message do módulo Aws::SQS::Client, espera receber 2 parâmetros: queue_url e receipt_handle. Este último a gente recebe do método receive_message. Logo, fica a dica: Não descarte o atributo receipt_handle que vem com cada uma das mensagens recuperadas.
describe "When called delete_message" do
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.delete_message({queue_url: "", receipt_handle: "something"}) }.to raise_error(ArgumentError)
end
it "Should return ArgumentError if receipt_handle is invalid" do
expect { @client_aws_sqs.delete_message({queue_url: queue_url, receipt_handle: nil}) }.to raise_error(ArgumentError)
end
it "Should return success" do
response = @client_aws_sqs.delete_message({queue_url: queue_url, receipt_handle: "something"})
expect ( response.successful? ).should be_truthy
expect ( response ).should eq(Aws::EmptyStructure.new)
end
end
O primeiro teste já sabemos de cór o que é, então vou direto para o segundo.
No segundo teste a gente verifica que, se o receipt_handle for nulo, ocorre um erro.
No terceiro a gente verifica se ocorreu tudo bem ao excluir a mensagem. A última condição deste teste verifica se o retorno corresponde à classe esperada.
Stub Aws SQS Client delete_message_batch
O método delete_message_batch da classe Aws:SQS:Client, espera receber 2 parâmetros: queue_url e entries. O primeiro parâmetro não preciso mais explicar.
O segundo parâmetro é um Array que deve conter Hashs como esta:
{id: "message_id", receipt_handle: "receipt_handle"}
Diferente do delete_message, o delete_message_batch exige que a message_id esteja presente junto com o receipt_handle. Ambos os atributos desta hash vêm na mensagem recuperada pelo receive_message.
describe "When called delete_message_batch" do
it "Should return ArgumentError if queue_url is invalid URL" do
expect { @client_aws_sqs.delete_message_batch({queue_url: "", entries: []}) }.to raise_error(ArgumentError)
end
it "Should return ArgumentError if entries is invalid" do
expect { @client_aws_sqs.delete_message_batch({queue_url: queue_url, entries: nil}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_message_batch({queue_url: queue_url, entries: ""}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_message_batch({queue_url: queue_url, entries: [{id: nil, receipt_handle: "something"}]}) }.to raise_error(ArgumentError)
expect { @client_aws_sqs.delete_message_batch({queue_url: queue_url, entries: [{id: "something", receipt_handle: nil}]}) }.to raise_error(ArgumentError)
end
it "Should return success" do
expect ( @client_aws_sqs.delete_message_batch({queue_url: queue_url, entries: [{id: "something", receipt_handle: "something"}, {id: "anything", receipt_handle: "anything"}]}).successful? ).should be_truthy
end
end
O primeiro teste já é bem conhecido…
O segundo teste garante que, caso entries seja inválido, um erro estourará. Lembrando que se alguma das verificações falhar, o teste falha.
O terceiro, a gente garante que ao passar os parâmetros corretos, nenhum erro ocorre.
Stub Aws SQS QueuePoller
A classe QueuePoller do módulo Aws SQS é muito interessante pois permite que a gente mantenha uma conexão persistente com o SQS, e vá consumindo as mensagens de forma mais rápida, uma vez que a conexão será feita apenas uma vez.
Por padrão o QueuePoller elimina as mensagens recebidas, porém podemos previnir a exclusão com o seguinte parâmetro e valor: skip_delete: true.
Também é importante passar o parâmetro max_number_of_messages:10 para que o poller pegue o máximo de mensagens que puder, por tentativa e o wait_time_seconds: nil para que a conexão não seja fechada rápidamente, assim permitindo que a aplicação fique por um longo período de tempo conectada ao SQS.
Também não esqueça do parâmetro visibility_timeout pois este parâmetro recebe o valor em segundos, que cada mensagem firacá in flight.
Deixa de conversa e vamos ao teste!
describe "When use poll" do
before do
@poller_aws_sqs = Aws::SQS::QueuePoller.new(queue_url, {max_number_of_messages:10, skip_delete: true, wait_time_seconds: nil, visibility_timeout: 60})
@poller_aws_sqs.before_request { |stats| throw :stop_polling if stats.received_message_count >= 2 }
@poller_aws_sqs.client.stub_responses(:receive_message, messages: [
{
message_id: "something",
receipt_handle: "something",
body: "something"
},
{
message_id: "anything",
receipt_handle: "anything",
body: "anything"
}
])
end
it "Should receive messages" do
received_messages = []
@poller_aws_sqs.poll do |messages, stats|
messages.each do |message|
received_messages << message
end
end
expect ( received_messages.length ).should eq(2)
expect ( received_messages.first.body ).should eq("something")
expect ( received_messages.last.body ).should eq("anything")
end
end
Primeira coisa que fizemos foi instânciar o Aws::SQS::QueuePoller e atribuí-lo à uma variável global para que possamos utilizá-la no teste:
@poller_aws_sqs = Aws::SQS::QueuePoller.new(queue_url, {max_number_of_messages:10, skip_delete: true, wait_time_seconds: nil, visibility_timeout: 60})
Após utilizamos o método before_request da classe Aws:SQS:QueuePoller para definirmos a condição de parada, pois sem ele, nosso teste ficaria em looping infnito, uma vez que configuramos o parâmetro wait_time_seconds: nil.
@poller_aws_sqs.before_request { |stats| throw :stop_polling if stats.received_message_count >= 2 }
Com esta linha, dissemos ao Aws:SQS:QueuePoller para que encerre a conexão e saia do looping, assim que a quantidade de mensagens recebidas for maior ou igual a 2.
Depois criamos o stub do Aws::SQS::Client do Aws:SQS:QueuePoller, para que duas mensagens sejam retornadas quando o poll chamar o receive_message:
@poller_aws_sqs.client.stub_responses(:receive_message, messages: [
{
message_id: "something",
receipt_handle: "something",
body: "something"
},
{
message_id: "anything",
receipt_handle: "anything",
body: "anything"
}
])
Daí declaramos uma variável para guardar as mensagens recebidas através do poll.
received_messages = []
E iniciamos o poll:
@poller_aws_sqs.poll do |messages, stats|
messages.each do |message|
received_messages << message
end
end
Basicamente ele entra no loop e ao receber um batch de mensagens, lê uma a uma e adiciona cada uma delas na variável received_messages.
Após testa a quantidade de mensagens recebidas e seus conteúdos:
expect ( received_messages.length ).should eq(2)
expect ( received_messages.first.body ).should eq("something")
expect ( received_messages.last.body ).should eq("anything")
Com isso garantimos que o poller funcionou e que retornou as mensagens que criamos no stub.
Conclusão
Como podemos perceber, não é tão difícil fazer testes automáticos, utilizando o RSpec e criando stubs para testar as funcionalidades do módulo Aws::SQS, do AWS SDK v2.
Links
Código-fonte: https://github.com/gabrielzuqueto/rspec_aws_sdk_v2_sqs
Aws SQS Client: http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html
AwS SQS QueuePoller: http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/QueuePoller.html
No próximo post vou mostrar como criar uma classe para que possamos utilizar o módulo Aws::SQS de forma simples em nossos projetos. Também vou mostrar como criar testes para a classe que iremos criar. Dessa forma, tudo visto aqui neste post terá mais sentido.
Livros indicados
É vital que um profissional de T.I conheça boas práticas e saiba aplicá-las independente da linguagem ou ferramenta.
Pensando nisso, separei alguns títulos que fazem parte da minha bilioteca pessoal.
Aproveite e invista na sua educação, pois é a base de tudo para uma carreira incrível.
Deixe seu comentário
Atenção: Os comentários abaixo são de inteira responsabilidade de seus respectivos autores e não representam, necessariamente, a opinião do autor desse blog.
Não perca mais nenhum post!
Cadastre-se e receba novos posts diretamente em seu e-mail.
Escolhidos para você