mutation testing - ruby edition

33
Mutation Testing Chris Sinjakli Ruby Edition

Upload: chris-sinjakli

Post on 20-Jun-2015

1.378 views

Category:

Technology


1 download

DESCRIPTION

Extended version of my brown bag lightning talk on Mutation Testing. Given at IPRUG on 3rd July 2012.

TRANSCRIPT

Page 1: Mutation Testing - Ruby Edition

Mutation Testing

Chris Sinjakli

Ruby

Edition

Page 2: Mutation Testing - Ruby Edition

Testing is a good thing

But how do we know our tests are good?

Page 3: Mutation Testing - Ruby Edition

Code coverage is a start

But it can give a “good” score with really dreadful tests

Page 4: Mutation Testing - Ruby Edition

Really dreadful testsclass Adder def self.add (x, y) return x - y endend

describe Adder do it "should add the two arguments" do Adder.add(1, 1) endend

Coverage: 100%Usefulness: 0

Page 5: Mutation Testing - Ruby Edition
Page 6: Mutation Testing - Ruby Edition

A contrived example

But how could we detect it?

Page 7: Mutation Testing - Ruby Edition

Mutation Testing!

“Who watches the watchmen?”

Page 8: Mutation Testing - Ruby Edition

If you can change the code, and a test doesn’t fail, either the code is never run or the tests are

wrong.

Page 9: Mutation Testing - Ruby Edition

How?

1. Run test suite2. Change code (mutate)3. Run test suite again

If tests now fail, mutant dies. Otherwise itsurvives.

Page 10: Mutation Testing - Ruby Edition

Going with our previous exampleclass Adder def self.add (x, y) return x - y endend

describe Adder do it "should add the two arguments" do Adder.add(1, 1) endend

Let’s change something

Page 11: Mutation Testing - Ruby Edition

Going with our previous exampleclass Adder def self.add (x, y) return x + y endend

describe Adder do it "should add the two arguments" do Adder.add(1, 1) endend

This still passes

Page 12: Mutation Testing - Ruby Edition

Success

We know something is wrong

Page 13: Mutation Testing - Ruby Edition

So what? It caught a really rubbish test

How about something slightly less obvious?

Page 14: Mutation Testing - Ruby Edition

Slightly less obvious (and I mean slightly)

class ConditionChecker def self.check(a, b) if a && b return 42 else return 0 end endend

describe ConditionChecker do it "should return 42 when both arguments are true" do ConditionChecker.check(true, true).should == 42 end it "should return 0 when both arguments are false" do ConditionChecker.check(false, false).should == 0 endend

Coverage: 100%Usefulness: >0But still wrong

Page 15: Mutation Testing - Ruby Edition

Slightly less obvious (and I mean slightly)

class ConditionChecker def self.check(a, b) if a && b return 42 else return 0 end endend

describe ConditionChecker do it "should return 42 when both arguments are true" do ConditionChecker.check(true, true).should == 42 end it "should return 0 when both arguments are false" do ConditionChecker.check(false, false).should == 0 endend

Mutate

Page 16: Mutation Testing - Ruby Edition

Slightly less obvious (and I mean slightly)

class ConditionChecker def self.check(a, b) if a || b return 42 else return 0 end endend

describe ConditionChecker do it "should return 42 when both arguments are true" do ConditionChecker.check(true, true).should == 42 end it "should return 0 when both arguments are false" do ConditionChecker.check(false, false).should == 0 endend

Passing tests

Page 17: Mutation Testing - Ruby Edition

Mutation testing caught our mistake

:D

Page 18: Mutation Testing - Ruby Edition

Useful technique

But still has its flaws

Page 19: Mutation Testing - Ruby Edition

The downfall of mutation(Equivalent Mutants)

index = 0

while index != 100 dodoStuff()index += 1

end

index = 0

while index < 100 dodoStuff()index += 1

end

Mutates to

But the programs are equivalent, so no test will fail

Page 20: Mutation Testing - Ruby Edition

There is no possible test which can “kill” the mutant

The programs are equivalent

Page 21: Mutation Testing - Ruby Edition

Also (potentially)

• Infinite loops• More memory used• Compile/run time errors – tools should

minimise these

Page 22: Mutation Testing - Ruby Edition

How bad is it?

• Good paper assessing the problem [SZ10]• Took 7 widely used, “large” projects• Found:– 15 mins to assess one mutation– 45% uncaught mutations are equivalent– Better tested project -> worse signal-to-noise ratio

Page 23: Mutation Testing - Ruby Edition

Can we detect the equivalents?

• Not in the general case [BA82]• Some specific cases can be detected– Using compiler optimisation techniques [BS79]– Using mathematical constraints [DO91]– Line coverage changes [SZ10]

• All heuristic algorithms – not seen any claiming to kill all equivalent mutants

Page 24: Mutation Testing - Ruby Edition

Tools

Some Ruby, then a Java one I liked

Page 25: Mutation Testing - Ruby Edition

Ruby

• Looked into Heckle• Seemed unmaintained (nothing since 2009)• Then I saw...

Page 26: Mutation Testing - Ruby Edition

Ruby

Page 27: Mutation Testing - Ruby Edition

Ruby

• Mutant seems to be the new favourite• Runs in Rubinius (1.8 or 1.9 mode)• Only supports RSpec• Easy to set up

rvm install rbx-headrvm use rbx-headgem install mutant

• And easy to usemutate “ClassName#method_to_test” spec

Page 28: Mutation Testing - Ruby Edition

Java

• Loads of tools to choose from• Bytecode vs source mutation• Will look at PIT (seems like one of the better

ones)

Page 29: Mutation Testing - Ruby Edition

PIT - pitest.org

• Works with “everything”– Command line– Ant– Maven

• Bytecode level mutations (faster)• Very customisable

– Exclude classes/packages from mutation– Choose which mutations you want– Timeouts

• Makes pretty HTML reports (line/mutation coverage)

Page 30: Mutation Testing - Ruby Edition

Summary

• Can point at weak areas in your tests• At the same time, can be prohibitively noisy• Try it and see

Page 31: Mutation Testing - Ruby Edition

Questions?

Page 32: Mutation Testing - Ruby Edition

References• [BA82] - T. A. Budd and D. Angluin. Two notions of correctness and

their relation to testing. Acta Informatica, 18(1):31-45, November 1982.

• [BS79] - D. Baldwin and F. Sayward. Heuristics for determining equivalence of program mutations. Research report 276, Department of Computer Science, Yale University, 1979.

• [DO91] - R. A. DeMillo and A. J. Outt. Constraint-based automatic test data generation. IEEE Transactions on Software Engineering, 17(9):900-910, September 1991.

• [SZ10] - D. Schuler and A. Zeller. (Un-)Covering Equivalent Mutants. Third International Conference on Software Testing, Verification and Validation (ICST), pages 45-54. April 2010.

Page 33: Mutation Testing - Ruby Edition

Also interesting• [AHH04] – K. Adamopoulos, M. Harman and R. M. Hierons. How to

Overcome the Equivalent Mutant Problem and Achieve Tailored Selective Mutation Using Co-evolution. Genetic and Evolutionary Computation -- GECCO 2004, pages 1338-1349. 2004.