Exceções em Ruby costumam ser classes bem simples. Utilizamos muitas das classes de exceções já existentes na linguagem quando desejamos testar um funcionamento mais técnico do sistema e criamos nossas próprias classes de exceções para representar com mais precisão o que ocorre na lógica de negócio.
Geralmente estas classes seguem o padrão de apenas possuir um nome e uma mensagem customizados - se tornando classes "vazias", sem conteúdo adicional. Porém, pode acontecer de precisarmos repassar algum dado adicional em conjunto com a mensagem de erro, que poderá ser utilizado por outra classe preparada para resgatar a exceção. Exceções também são objetos, então podemos simplesmente encapsular atributos adicionais no objeto de exceção, além da mensagem de erro, se considerarmos viável.
Quando escrevemos um teste com o RSpec para esperar o lançamento de uma exceção, nós temos que encapsular o código que causa a exceção dentro de um bloco.
expect do
starship.shoot
end.to raise_error(CannonDisabledError)
Com este formato também conseguimos validar facilmente o conteúdo da mensagem da exceção.
expect do
starship.shoot
end.to raise_error(CannonDisabledError, 'Cannon was disabled due to low energy.')
No entanto, somente com esta declaração, não conseguimos verificar os conteúdos adicionais que a exceção pode conter. Para fazer isso é necessário inserir outro bloco no final da expectativa, que serve para avaliar o objeto da exceção que foi lançada.
expect do
starship.shoot
end.to raise_error(CannonDisabledError) do |error|
# a partir daqui avaliamos o objeto da exceção
expect(error.remaining_ammo).to eq(10)
expect(error.damaged?).to eq(false)
end