Fork me on GitHub

Keep Learning Conhecimento nunca é o bastante

Postado em
18 March 2009 @ 13:45

Tag(s)
Anúncios, Desenvolvimento, Ruby, Test-Driven Development

Evitando over-stubbing com Mocha

Não é segredo que não sou “fã” da maneira como a comunidade de desenvolvedores utiliza mocks e stubs. A meu ver, trata-se de mal uso de uma ferramenta muito útil.

Com esse tipo de uso surgem alguns problemas, tais como over-mocking e over-stubbing, ou seja, o uso abusivo de mocks e stubs. O abuso de mocks torna seus testes quebradiços, isto é, testes que falham sem que haja alteração de comportamento do componente sob verificação. O abuso de stubs torna seus testes fracos, isto é, testes que saem de sincronia com o código, verificando comportamento que já não é real, mas continuam passando.

Um exemplo de abuso de mocks:

it "should be successful" do
  Account.expects(:new).with(:current_user => @user, :first_name => "Test", :last_name => "Test").returns(@account)
  @account.expects(:login_method=).with(CONFIG::LOGIN::OpenID)
  @account.expects(:has_email_and_password=).at_least_once
  @account.expects(:has_openid=).twice
  do_edit
  response.should be_success
  response.should render_template("edit")
end

Note que até métodos de atribuição são colocados sob expectativas. Se a implementação sofrer uma alteração simples, fazendo, por exemplo, com que algum desses métodos não seja mais chamado, mesmo que não haja qualquer modificação no comportamento, o teste falhará.

Um exemplo do abuso de stubs:

it "has been modified a lot of times" do
  Dependency.stubs(:a_method)
  NoLongerADependency.stubs(:something).returns(false)
  AnotherDependency.stubs(:nonexistent_method).returns(10)
  MyClass.new.do_something
end

Com esse tipo de abordagem, você pode acabar com problemas como fazer stubs de métodos que não existem mais ou até mesmo de objetos que já não são mais uma dependência.

Uma forma de evitar isso com o Mocha (além, é claro, de estudar o uso correto das ferramentas) é utilizar algumas configurações providas pelo framework através de sua classe Configuration:

Mocha::Configuration.prevent(:stubbing_non_existent_method) #impede stubbing de métodos inexistentes
 
Mocha::Configuration.prevent(:stubbing_method_unnecessarily) #impede stubbing de métodos não utilizados
 
Mocha::Configuration.prevent(:stubbing_non_public_method) #impede stubbing de métodos não-públicos
 
Mocha::Configuration.prevent(:stubbing_method_on_non_mock_object) #impede stubbing em objetos "reais"

Nos exemplos acima, caso encontre um dos usos “indevidos”, o Mocha lançará uma exceção do tipo Mocha::StubbingError. Isso ocorre por termos utilizado o método prevent. Há outras configurações possíveis, sendo providos três nívels de configuração:

# padrão - permite a condição
Mocha::Configuration.allow(condition)
 
# apenas emite um aviso quando ocorre a condição
Mocha::Configuration.warn_when(condition)
 
# lança um erro quando ocorre a condição
Mocha::Configuration.prevent(condition)

Essas configurações podem ser um auxílio para o aprendizado do uso correto e equilibrado da ferramenta.

Leia mais:
Mocha::Configuration RDoc
Mocha Configuration – James Mead


7 Comentários

Comentário por
Tapajós
18 March 2009 @ 22:09

Lucas, parabéns pelo post, gostei bastante.

[]’s


Comentário por
Lucas Húngaro
18 March 2009 @ 22:44

Obrigado Tapajós! 🙂


Comentário por
Thiago Pradi
20 March 2009 @ 10:55

Belo post!

vc sabe se existe algo parecido para o Framework de Mocking do RSpec?

Abraço


Comentário por
Flavio Granero
20 March 2009 @ 12:32

Muito legal o texto, parabéns.
Acho esta distinção entre estar abusando ou não do uso de mockings e stubs um tanto difícil, por que normalmente faço uso deles para além de testar, aumentar a velocidade de testes funcionais, evitando requisições ao banco de dados. Como você vê estes casos?

Abraço


Comentário por
Lucas Húngaro
20 March 2009 @ 17:26

Thiago, obrigado! Infelizmente, não sei se o RSpec possui algo semelhante.

Flavio, em alguns casos é difícil mesmo. Minha guideline é evitar fake objects sempre que possível. Só uso, por exemplo, se é inviável acessar algum recurso externo à aplicação como, por exemplo, um gateway de pagamento sem um sandbox (o que já é um mau sinal). Sobre a prática de usar mocks/stubs pra evitar acesso ao banco de dados, eu não gosto e sempre recomendo evitar, pois leva a testes muito fracos e/ou quebradiços.


Comentário por
phpsaux
3 April 2009 @ 16:15

Bem maneiro o seu post, gostei bastante. Se liga, acessei seu dominio direto e vi que esta sem index. Acerta la! 😀

http://www.makemesimple.com


Comentário por
Lucas Húngaro
4 April 2009 @ 1:40

Opa, valeu phpsaux!

Sobre o index, eu deixei sem mesmo. Provavelmente vou mudar o domínio e não liguei de deixar assim. Mas acho que vou colocar um redirecionamento pro blog hehehe.


Deixe um comentário