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