The Mutation Game: Cracking the Enigma of Mutation Testing

Summarized using AI

The Mutation Game: Cracking the Enigma of Mutation Testing

Tyler Lemburg • November 13, 2024 • Chicago, IL • Talk

In the presentation titled "The Mutation Game: Cracking the Enigma of Mutation Testing" at RubyConf 2024, speaker Tyler Lemburg explores the concept of mutation testing in programming, particularly focusing on Ruby applications. Mutation testing is framed as a game where the goal is to ensure that tests are sufficiently robust to catch various alterations to the code. Lemburg outlines the following key points during his talk:

  • Definition of Mutation Testing: Mutation testing involves modifying code in various ways and running unit tests on the altered code to check if they fail. Successful tests indicate that the code is not adequately tested.

  • Benefits: The key benefits of mutation testing include improved test coverage, cleaner code, and an enhanced understanding of Ruby's inner workings. Tests serve to validate the full functionality of code, thus increasing confidence in the unit tests.

  • Tools for Mutation Testing: Lemburg highlights the 'mutant' gem as the primary tool for mutation testing in Ruby, which parses Ruby code into an abstract syntax tree and applies a variety of mutations to check test results.

  • Demonstration: A live demonstration examines a sample Ruby application where mutation testing is applied to a method to enhance test cases and improve code quality. For instance, Lemburg illustrates how to handle various edge cases by altering the code and observing the outcomes of the tests.

  • Limitations and Caveats: Despite its benefits, mutation testing has drawbacks, such as a potential slowdown in development cycle due to added complexity, occasional technical issues with the mutation tools, and constraints regarding legacy codebases. Lemburg advises caution in applying mutation testing to large, legacy systems but encourages its use in new projects or smaller services for optimal results.

  • Conclusion: Lemburg emphasizes that mutation testing is a valuable approach for ensuring robust, error-free code when appropriately applied. The overall message is about the importance of thorough testing in software development and the knowledge gained through challenging the code with mutation testing.

This engaging session not only [promotes mutation testing] as a powerful technique to enhance code quality but also encourages developers to think critically about their testing practices, ultimately leading to stronger, cleaner code.

The Mutation Game: Cracking the Enigma of Mutation Testing
Tyler Lemburg • November 13, 2024 • Chicago, IL • Talk

Can your application's code achieve complete perfection? If you join Professor X's School for Gifted Mutation Testers, it can get pretty dang close. Discover the Mystique of transforming your code in silly (and not-so-silly) ways, and seeing if this makes your tests fail. If your tests do fail, they are solid as a Colossus! If your tests passed, then you have discovered a Rogue mutant! But do not worry: I will teach you the ins and outs of squashing that mutant like a Blob and making your code stronger than Wolverine.

Storm into this session and learn what mutation testing is all about, see if it may be right for your Ruby codebase, and explore the tools that make it possible. We will use the `mutant` gem and delve into an example Ruby app, bringing it to full mutation testing coverage through simplifying code and improving tests. Even if this technique is not right for your project, you will come away from this session with a deeper understanding of Ruby, code parsing, test-driven development, and writing clean, beautiful code.

RubyConf 2024

00:00:15.080 thank you for the warm welcome and
00:00:16.800 thanks to everyone for being here when
00:00:18.240 Patterson is talking upstairs I assure
00:00:20.760 you I'll inject as much comedy as
00:00:22.439 possible into this talk to make up for
00:00:25.679 it um yes I'm an engineering manager at
00:00:29.039 a company called on uh where we are sort
00:00:32.320 of the pipes between uh or health
00:00:34.480 insurance were the pipes between the
00:00:35.879 benefit administrators that you sign up
00:00:37.640 with benefits with um and the actual
00:00:39.719 health insurance
00:00:41.239 carriers um and uh so yeah if your claim
00:00:43.800 is denied uh it's it's probably my
00:00:47.640 fault well today we're going to be
00:00:49.399 talking about the mutation game uh Alan
00:00:52.280 touring called his touring test the
00:00:54.480 imitation game and uh I call mutation
00:00:57.199 testing the mutation game uh and it's
00:00:59.719 cuz truly is a game uh it's a game where
00:01:02.160 I am trying to get my code and my tests
00:01:04.320 to match up I'm working with my code I'm
00:01:06.439 wrangling my code and by the end of it
00:01:08.320 I'm usually uh trying to make sure that
00:01:10.560 I'm actually smarter than the computer
00:01:12.439 so all right well I can dig right in uh
00:01:15.200 we have an outline slide right now where
00:01:17.000 I'll be telling you what we'll be
00:01:18.000 talking about so uh we're going to be
00:01:19.840 talking about uh first of all what is
00:01:21.520 mutation testing uh exactly um I'll tell
00:01:24.200 you about the benefits of it uh you know
00:01:26.159 why I use it what what it's good for uh
00:01:28.320 we'll dig into the tools for mutation
00:01:30.159 testing and how those tools work uh so
00:01:32.960 I'll do a little bit of Deep dive there
00:01:34.840 uh then we'll have a demo assuming we
00:01:36.600 can get the uh technical difficulties
00:01:38.479 worked out um I'll go through some
00:01:40.360 examples some common gotchas uh show you
00:01:43.040 how it can be awesome and frustrating at
00:01:44.880 the same time and then I'll talk about
00:01:46.640 the uh limitations and caveats uh of
00:01:49.280 mutation testing so that you can know if
00:01:51.079 it's right for your code base and
00:01:52.360 whether whether you should implement it
00:01:53.759 um at work or at home what is mutation
00:01:56.039 testing basically it's testing your code
00:01:58.439 by altering it in or not so silly ways
00:02:01.840 and then you run your unit tests on that
00:02:04.240 altered code so uh what you're doing is
00:02:07.520 basically confirming that your code
00:02:09.360 works with the tests by by changing up
00:02:11.360 your code and seeing if your tests fail
00:02:13.480 so ideally once you mutate the code uh
00:02:16.160 automatically with your tools and run
00:02:18.480 tests on that mutated code your tests
00:02:20.400 will then fail or raise an error so if
00:02:23.000 your tests still pass then you have uh
00:02:26.319 one of one of three outcomes outcome one
00:02:28.440 is that like that alteration to the code
00:02:31.160 is a simplification that you can adopt
00:02:33.519 so right it just makes the code simpler
00:02:35.200 your code was doing too much and you you
00:02:37.000 don't want to test anymore than that um
00:02:39.680 option two is that your tests do not
00:02:41.560 cover the full functionality of the the
00:02:44.040 code that you've written right so it's
00:02:45.640 basically you you've simplified your
00:02:47.440 code with a mutation but um the uh the
00:02:51.599 the tests aren't aren't uh covering all
00:02:53.879 the things that you want to cover in
00:02:54.879 your in your code or the third option or
00:02:57.159 the third outcome is that the alteration
00:02:58.640 to that code is really quite quite silly
00:03:00.680 and so then you need to make a silly
00:03:01.920 change to your test case to make this
00:03:03.760 silly mutation fail that test case yes
00:03:06.799 thank
00:03:08.640 you all right so outcome number three is
00:03:11.480 where it really gets uh frustrating and
00:03:13.200 annoying and and uh that's that's that's
00:03:15.519 how that works so you're probably
00:03:17.200 wondering at this point what the heck is
00:03:18.360 this guy talking about so let's just do
00:03:20.400 a quick example um on the right I have a
00:03:23.599 method called get X-Man um it basically
00:03:26.280 is just uh making a key and pulling a
00:03:28.840 value out of a hash
00:03:30.319 and then I've written a spec for it um I
00:03:32.239 Define a hash here and then describe my
00:03:34.920 method and it gets an X-Men out of the
00:03:36.799 hash uh expects getting that person to
00:03:39.720 return Kurt Wagner okay this test looks
00:03:42.560 good this test certainly passes but have
00:03:44.799 I tested the output of this method if
00:03:46.519 the key does not exist well if I Want My
00:03:49.519 Method get xman to handle nil or return
00:03:52.439 nil then I need a test case for that
00:03:55.400 otherwise what mutant uh what mutation
00:03:58.120 testing is going to do is replace this
00:04:00.560 hash access the brackets uh or yeah the
00:04:03.879 brackets with a fetch method hash fetch
00:04:07.319 will throw an error if the key is not
00:04:09.360 present and so that's a more specific
00:04:11.200 method that I can adopt if I don't want
00:04:13.040 to I if I'm just not worried about the
00:04:15.040 case where the mutant is not present so
00:04:17.880 this is what mutation testing will catch
00:04:19.359 for you uh it'll catch cases where you
00:04:21.639 have left uh parts of your code open to
00:04:25.040 uh interpretation that you may not have
00:04:26.560 intended and give you the tools to uh
00:04:29.199 simplify solve it or test it if you if
00:04:31.479 you need
00:04:33.240 to all right so let's talk about the
00:04:35.560 benefits of this the benefits are that
00:04:37.560 you get more complete tests you know you
00:04:39.320 know your code is covered it's really
00:04:40.840 more of for one thing you're getting uh
00:04:43.759 uh code coverage Allah simple C and so
00:04:46.840 like you don't even need a tool like
00:04:48.080 that if you have mutation testing
00:04:49.400 because you are basically ensuring that
00:04:51.360 your code is covered in fact it's really
00:04:53.080 more of a functionality coverage than
00:04:55.199 just a line by line code coverage uh you
00:04:58.199 also get cleaner code you know that your
00:05:00.639 code and it's your tests are lining up
00:05:02.639 and uh mutation testing kind of forces
00:05:04.600 your code to be just smart uh clean uh
00:05:08.280 you know without without Frills and it
00:05:10.479 just get but still gets the job done uh
00:05:13.080 you also get like improved knowledge of
00:05:14.680 Ruby and its inner workings so mutation
00:05:16.759 testing will really kind of train you
00:05:18.360 and and give you uh uh a better deeper
00:05:21.479 knowledge of like kind of what ruby is
00:05:22.800 doing under the hood and it also trains
00:05:24.840 your mind to code and test without
00:05:26.759 potential mutations so as you get better
00:05:28.639 at this and do it more you'll find that
00:05:30.479 you're starting to write fetch all the
00:05:32.440 time instead of uh ever using uh hash
00:05:35.360 access with brackets uh you'll find that
00:05:37.680 you're you're writing your code cleaner
00:05:39.520 and you're writing it so that it is uh
00:05:42.120 you know just able to be tested smartly
00:05:44.759 simply and
00:05:46.000 easily you also get increased unit test
00:05:48.479 confidence you know that your tests are
00:05:49.840 lined up with your code you know that
00:05:51.319 your tests are covering everything that
00:05:52.759 your code is doing and really flaky
00:05:55.520 tests are all but eliminated it's pretty
00:05:57.520 hard to write a flaky test because
00:06:00.240 mutation testing is doing a lot of
00:06:02.319 different mutations onto each method and
00:06:04.080 running that code through uh all your
00:06:05.880 test cases and so if you have a flaky
00:06:08.319 test it's really going to be caught by
00:06:10.039 mutation testing um almost almost
00:06:14.400 certainly so let's talk about tools for
00:06:16.720 this really the only comp complete tool
00:06:19.120 for mutation testing in Ruby is the
00:06:20.840 mutant Gem and uh I'll talk about crude
00:06:24.199 mutant in a little bit but the mutant
00:06:26.160 gem uses the parser gem to parse Ruby
00:06:28.280 code into an abstract Sy tax treat so
00:06:30.960 that's basically a uh just a set of
00:06:33.479 instructions uh that represents your
00:06:35.400 Ruby code you know say if you're
00:06:37.000 accessing a hash it'll say uh take the
00:06:39.280 hash object send the access uh to it the
00:06:43.280 access method to it uh with a string the
00:06:45.680 string is created by interpolating uh
00:06:47.840 some Stuff Etc it it's basically an
00:06:50.639 abstract breakdown of what your code is
00:06:52.160 doing in a in a more simple form and so
00:06:55.080 with that the mutant gem has a giant
00:06:57.160 list of potential mutations which it can
00:06:59.879 monkey patch your methods with the
00:07:01.840 mutations to that code and then it runs
00:07:04.720 the test assigned to that subject as I
00:07:06.360 mentioned before but it only runs the
00:07:08.240 tests uh for that subject when it's
00:07:10.080 mutating that code so for example if I
00:07:11.759 have a method described Turing test um
00:07:14.199 or a test case describ Turing test it's
00:07:16.560 only going to mutate and run specs for
00:07:18.599 that uh method specifically it's not
00:07:20.599 going to mutate other things that that
00:07:22.039 won't affect it and so of course if my
00:07:24.879 tests failed then the mutation is killed
00:07:27.319 but and then my mission is to eliminate
00:07:29.560 all the mutations by adding more tests
00:07:31.280 or removing or simplifying code as I
00:07:33.240 talked about before so the that's how
00:07:35.720 mutant Works crude mutant Works mostly
00:07:37.759 the same way I think that's a much
00:07:39.120 smaller side project uh it basically
00:07:41.319 works by but instead of uh mutating code
00:07:43.960 it essentially just removes lines and
00:07:45.759 sees if your tests fail with that um so
00:07:48.360 that's kind of closer to the simple
00:07:49.919 cover code coverage uh tools out
00:07:53.440 there all right it's time for the demo
00:07:56.400 and for the demo we're going to be
00:07:57.919 playing um a little game
00:08:00.039 called Benedict versus
00:08:02.879 X-Men so uh Benedict Cumberbatch of
00:08:05.759 course from the movie The imitation game
00:08:08.680 and the X-Men of course because they are
00:08:10.440 they are mutants and uh we are mutating
00:08:12.400 code so uh take a moment to familiarize
00:08:15.319 yourself with the uh movies of each
00:08:18.919 category as shown
00:08:22.080 here now we want this to be a Fair
00:08:24.319 competition
00:08:28.199 so we will first check to ensure that uh
00:08:31.360 the arrays of our data are the same
00:08:33.440 size all right website confirms that the
00:08:36.880 arrays are the same size we're going to
00:08:38.320 be looking at Benedict cumberbatch's
00:08:39.959 last 14 movies non-document and then all
00:08:43.000 14 of the X-Men movies uh to dates all
00:08:46.399 right well let's check on check our code
00:08:48.080 on this method and see if uh mutation
00:08:50.040 testing tells us anything so on the left
00:08:52.720 I have a calc class which does a lot of
00:08:55.519 uh very simple methods that are just
00:08:57.040 implementing some very simple things and
00:08:58.839 on the right I have a test suite for
00:09:00.240 that class um this test Suite is fully
00:09:03.160 passing so arrays same length uh we have
00:09:07.279 or just checking whether the the first
00:09:09.120 array has the same length as the second
00:09:11.040 array um I will run a mutant command by
00:09:15.000 uh doing it like this I'm just mutating
00:09:16.640 this method only that's why I'm
00:09:18.040 specifying uh with the uh after the
00:09:20.680 Double Dash I'm specifying array same
00:09:22.360 length
00:09:32.279 all right all right so mutant has given
00:09:35.160 us a bunch of output here uh you can
00:09:37.360 kind of read into this stuff it shows
00:09:39.360 how many tests it found so it found six
00:09:41.600 tests those were that's like all the
00:09:43.399 tests for that class entirely but you
00:09:45.360 can see that it only selected one test
00:09:47.680 um which is the the array same length
00:09:49.440 test here that I've that I've
00:09:51.120 created um and there were 20 different
00:09:53.600 mutations that it tried and let's see
00:09:55.560 what it came up with so it there were 18
00:09:58.839 kills uh but two mutants mutations Left
00:10:01.800 Alive and so let's see what those
00:10:04.519 are all right so it's saying what it's
00:10:07.079 saying here is that I could change this
00:10:09.079 line by replacing double equals with
00:10:11.800 equal and get the same result that is to
00:10:14.360 say my tests would still pass um
00:10:16.560 equivalently down here it would say I
00:10:18.160 can replace with equal uh and my test
00:10:20.760 would still pass as well okay well this
00:10:22.640 makes sense because in Ruby uh integers
00:10:26.680 an integer with with the same number uh
00:10:28.560 is basically a Singleton and represented
00:10:30.440 by this exact same object um uh VM wide
00:10:33.920 basically so essentially equal which
00:10:36.600 basically uh Compares whether it's the
00:10:38.240 exact same object is more specific than
00:10:40.480 the double equals up here and so I can
00:10:42.760 just replace that uh and this will since
00:10:46.240 I'm I know I want to compare two
00:10:47.560 integers here um it'll always work that
00:10:49.800 way all right let's mutate again all
00:10:53.160 right still one
00:10:54.600 issue aha it's saying that I can just
00:10:57.120 replace this entire uh line with true
00:11:01.519 well looking at this I guess that makes
00:11:03.680 sense because my test cases are only
00:11:06.320 testing whether I get true out of this
00:11:08.279 boie method uh so really what I need to
00:11:11.000 do is make another test case that
00:11:12.560 actually returns
00:11:21.320 false I need to change that to false
00:11:25.680 also all right one last mutation on this
00:11:28.040 method and now mutant coverage is 100%
00:11:31.079 so we've we've been able through that
00:11:32.480 we've been able to ensure that like all
00:11:34.240 right our method is fully tested we've
00:11:36.320 got a very specific uh check on whether
00:11:38.800 these arrays are the same length and uh
00:11:40.880 we feel we feel pretty good about this
00:11:43.160 method all right now let's get into it
00:11:46.839 so Benedict cumberbatch's last 14 movies
00:11:50.240 or the 14 X-Men movies which has the
00:11:52.920 higher average box
00:11:55.639 office who thinks Benedict raise your
00:11:57.839 hand
00:12:00.079 all right and who thinks the
00:12:02.200 X-Men much more for the X-Men and you
00:12:04.920 are correct uh it's fairly close 530
00:12:08.079 million for the X-Men 469 for Benedict
00:12:10.560 Cumberbatch uh you can tell that
00:12:12.639 Benedict Cumberbatch was probably helped
00:12:14.160 out a lot here by Avengers endgame uh
00:12:16.839 but you know that's that's how that
00:12:18.600 that's that's how it goes let's look at
00:12:20.800 our average method uh and mutate it a
00:12:24.639 bit so uh my average method here um it's
00:12:28.639 creates a sum uh adds the each value of
00:12:31.760 the array into the sum and then uh
00:12:34.160 outputs the L sum divided by the the
00:12:37.839 length and my test on the right is just
00:12:40.880 doing a quick test of
00:12:43.839 um some integers
00:12:46.000 there all right my Mutant is telling me
00:12:48.440 there's some differences here let's
00:12:49.800 scroll up a little bit oops didn't mean
00:12:52.360 to do
00:12:53.959 that there we go uh ah I have this puts
00:12:58.760 going on here now this is an easy way
00:13:01.760 for me mutation testing to find code
00:13:03.440 that you don't need in there right it
00:13:05.399 doesn't matter what I can change this
00:13:06.800 puts line to it's not doing anything and
00:13:08.800 it's not being tested so I can just
00:13:10.880 remove this uh because I probably didn't
00:13:12.639 mean to have it in there anyway it's
00:13:13.800 probably just for debugging if you did
00:13:15.560 need to have a puts or a logging uh
00:13:18.160 statement in your code then yes you will
00:13:19.920 have to have a test case saying that you
00:13:21.360 know standard out or your logging class
00:13:23.440 or whatever is receiving a
00:13:25.639 message um the other note that mutation
00:13:28.199 testing gave me here is is that I could
00:13:29.760 remove the 2f uh at the bot at the back
00:13:32.639 here but I want that 2f because I want
00:13:34.680 you know float division not integer
00:13:36.399 division so I realized the issue here is
00:13:39.120 that I'm just I'm just doing all
00:13:40.240 integers here and I'm ending up with an
00:13:41.560 integer um at the end of my test case so
00:13:44.279 let me add a quick new one here where it
00:13:45.920 where it does return a float um and that
00:13:49.399 should probably solve
00:13:51.000 it indeed it
00:13:55.040 does all right Benedict Cumberbatch or
00:13:57.920 the X-Men which is the higher median box
00:14:01.079 office of their 14
00:14:03.680 movies who thinks Benedict
00:14:06.920 Cumberbatch okay a few more who thinks
00:14:11.240 X-Men oh I'm afraid a lot of you were
00:14:13.720 wrong on this
00:14:15.480 one uh the X-Men were the X-Men movies
00:14:18.160 were pretty consistently Blockbusters
00:14:20.399 Benedict cumberbatch's movies some
00:14:22.560 Blockbusters some not so much uh yeah so
00:14:26.639 uh you know there he did a lot of uh did
00:14:28.560 a lot of like smaller roles in um uh
00:14:30.560 it's kind of smaller some smaller movies
00:14:32.839 I think and and we'll show the data in a
00:14:34.440 little bit but let's look at our median
00:14:36.560 method
00:14:38.600 next um I'm just taking an array here
00:14:41.240 and finding the uh you know sorting it
00:14:44.440 and then of course picking the middle
00:14:46.040 value and I have a easy test over here
00:14:49.040 as well let's test let's run
00:14:56.560 mutant okay the first thing I no notice
00:14:59.279 is that uh it's reporting I can just use
00:15:01.680 fetch so same as hashes arrays can use
00:15:04.279 fetch to um ensure that that uh index is
00:15:07.759 in the array and if it's not there it'll
00:15:09.160 throw an error so let's just assume that
00:15:11.199 yeah that's that's kind of what I want
00:15:12.519 so let's let's replace that with
00:15:16.519 fetch um here's a funny mutation it took
00:15:19.360 array length divided by two and just
00:15:21.800 replaced with the value to and I still
00:15:24.120 got a passing test case well that's
00:15:26.480 funny because my that works here because
00:15:29.040 because I have a five value array and um
00:15:33.040 I have equal you know it's looking at
00:15:35.040 the middle value so divide by two the
00:15:37.880 second uh 012 second index so let me add
00:15:41.040 something else in here let me just throw
00:15:42.839 in something um I don't know a little
00:15:44.839 bigger maybe and see if that helps
00:15:47.120 it so the median here should be four now
00:15:50.399 and let's run mutant mutant
00:15:52.720 again a little better let's scroll
00:15:55.920 up it's saying I can go do away with the
00:16:00.160 sort okay so that makes sense because
00:16:03.160 the the test arrays that I'm using over
00:16:04.839 here are in fact already sorted so I can
00:16:07.839 uh alleviate this by you know throwing
00:16:09.720 something in there where uh let me just
00:16:11.839 kind of unsort them a little bit here um
00:16:14.759 make sure that I
00:16:16.279 am doing that one more mutant and uh
00:16:21.199 we're good to go in this
00:16:22.839 Method All right well let's take a look
00:16:25.040 at the actual data so that uh you can
00:16:27.519 see that I was I was was maybe being a
00:16:29.720 little factious or tricky with you
00:16:31.600 because uh this uh mowgly legend of the
00:16:34.880 jungle for bened Cumberbatch it was on
00:16:37.120 Netflix and so uh had zero box office uh
00:16:40.720 similarly the power of the dog uh I
00:16:43.040 think it must have gotten a limited
00:16:44.079 release so
00:16:45.639 700,000 of box office there but of
00:16:47.880 course uh the big ones Doctor Strange
00:16:50.279 nearly a billion Spider-Man three nearly
00:16:52.880 two billion uh and then Avengers endgame
00:16:55.000 2.7 billion uh Dr Seuss The Grinch came
00:16:57.920 out pretty high as well and 1917 also
00:17:00.600 also good ones but you know you can see
00:17:02.680 with the X-Men movies that was where the
00:17:05.039 real the real hay was made they started
00:17:07.280 off strong got even stronger and then
00:17:09.600 really came into their own once once
00:17:11.160 Deadpool came out uh 700 million there
00:17:14.079 700 million for Deadpool 2 and then
00:17:15.959 Deadpool and Wolverine 1.3 billion so uh
00:17:20.280 that they have they have amassed more
00:17:21.959 than uh the totals for for Benedict
00:17:24.000 Cumberbatch
00:17:25.679 there all right now you've all memorized
00:17:28.360 those numbers I'm sure which of these
00:17:30.679 box office numbers are valid UPC
00:17:35.400 codes any guesses of how many out of 28
00:17:38.400 shout out a number
00:17:40.480 three
00:17:42.360 28
00:17:44.000 seven the answer is two actually uh I I
00:17:49.360 padded with zeros in the case that uh
00:17:52.039 all in the case there was not enough
00:17:54.039 digits to make an 8-digit UPC code uh
00:17:56.440 but only one uh movie aside from side
00:17:59.039 from mowgly uh came out with a valid UPC
00:18:01.440 and that was that was Deadpool one so
00:18:04.120 all right let's look into our code for
00:18:05.320 this
00:18:06.360 then so I've got a method that checks
00:18:08.679 whether I have a valid UPC check digit
00:18:11.559 uh uh for eight digigit UPC um on an
00:18:15.200 array uh let's see I which to calculate
00:18:18.480 a UPC code or check it basically you
00:18:20.880 take the the values in slots 02 46
00:18:25.120 multiply them by three and then add the
00:18:27.039 slots that are in the even or the even
00:18:28.840 unit parts so slots 1 three and five uh
00:18:31.520 mod that all by 10 and then it has to
00:18:33.440 equal the final digit I I used inject
00:18:36.120 here and I didn't use it here don't ask
00:18:37.840 me why I'm I'm a manager now so I don't
00:18:39.679 I don't it doesn't
00:18:41.400 matter um and I've learned my lesson now
00:18:43.760 I've used fetch to get out of the get
00:18:45.559 stuff out of the arrays I have WR
00:18:48.200 written several test cases here that
00:18:49.919 return true true and false on this
00:18:51.600 Boolean method I think I should be good
00:18:53.520 here let's see what happens
00:19:10.360 all right still five mutants alive let's
00:19:12.200 see what
00:19:13.960 happened all right interesting so here
00:19:16.919 it's replaced my five in this uh
00:19:19.799 statement with one and it's saying I can
00:19:22.200 still pass my test cases that way all
00:19:24.360 right let's look my test data has the
00:19:27.159 same stuff in uh in Array index five as
00:19:30.520 it does in slot one in every case and so
00:19:34.039 that's why it's saying like hey you can
00:19:35.600 just you know use that so here's a case
00:19:37.640 where you have good data and you have
00:19:39.799 good test cases you know certainly
00:19:42.039 sufficient test cases but it's going to
00:19:44.159 gamify you and make you like change
00:19:46.280 things up a lot just in order to satisfy
00:19:48.840 its its weird mutations so let me add
00:19:51.720 four here and I'll add four to the end
00:19:53.280 of this uh as well let's try
00:19:57.480 again okay a little better I got rid of
00:19:59.840 that one uh oh same problem with index 3
00:20:03.200 that's fine let's just uh add two there
00:20:06.320 add two there um I'll scroll up and look
00:20:08.960 at another one here oh here it's saying
00:20:11.720 array fetch one can just be replaced
00:20:13.880 with the number one well that's because
00:20:15.400 I used one in every uh one index slot
00:20:18.320 here so okay let me just uh compute
00:20:20.840 change this up as well uh this is you
00:20:23.440 know if I had three here then I should
00:20:24.880 add three over here too uh let's scroll
00:20:27.840 up a little bit more oh it's saying with
00:20:29.840 these these integers here I could change
00:20:31.679 the four to a zero and it still works so
00:20:33.679 oh so any any copying of data over here
00:20:36.280 is like totally making it uh flip and
00:20:39.120 here's here's the part where I'm talking
00:20:40.400 about where it gets frustrating and it's
00:20:42.159 like I I know I have this tested right
00:20:44.360 but uh mutation testing is telling me I
00:20:46.200 don't
00:20:48.280 um that's the the part you just have to
00:20:50.520 deal
00:20:52.880 with all right demo over so let me just
00:20:56.880 talk for a little bit about the
00:20:58.039 limitations and caveats uh despite the
00:21:00.080 one I just talked about uh the thing
00:21:02.360 with mutation testing is that you're
00:21:03.880 it'll slow your development down uh this
00:21:05.880 makes sense because you're being more
00:21:07.039 sure and more more certain of what
00:21:08.640 you're doing but you also have
00:21:10.080 occasional unknown gotches or Hang-Ups
00:21:12.520 not dissimilar to the one that I just
00:21:14.080 showed but also sometimes the mutant gem
00:21:16.679 will throw up weird errors because of
00:21:18.600 its threading um sometimes it will uh
00:21:21.760 just like freak out and you just you
00:21:23.679 have to like understand what's going on
00:21:25.720 what spec it's running and and find the
00:21:27.720 find the error
00:21:29.400 another thing is that it's slow so you
00:21:31.279 notice that it's running a lot of it's
00:21:32.919 running the same test Suite A lot of
00:21:34.400 different times uh for each mutation and
00:21:37.360 even if you do this just for the code
00:21:39.200 that you change in that in a certain
00:21:40.840 commit uh which is how you should
00:21:42.400 configure it um it has options for that
00:21:45.159 um but even if you do that it can take
00:21:46.520 just as long or longer than your normal
00:21:47.919 test Suite so uh it's it's a slower
00:21:50.559 process that definitely takes up
00:21:52.760 Cycles um and it requires code in tests
00:21:55.320 with few or no external dependencies uh
00:21:57.880 this is okay though because it kind of
00:21:59.000 trains your mind for
00:22:00.720 isolation uh the mutant gem can mutate
00:22:03.320 custom dsls and mixins but testing those
00:22:05.480 can be confusing uh it can also uh or
00:22:08.760 sorry it cannot mutate methods defined
00:22:10.559 within a closure or an eval so because
00:22:12.679 you're dynamically defining those
00:22:14.080 methods the uh parser gem will not parse
00:22:16.840 them out as part of the abstract syntax
00:22:18.799 tree right like it won't it won't be
00:22:20.400 able to know what's exactly in those
00:22:22.000 methods because they're dynamically
00:22:23.320 defined and thus even though you have a
00:22:25.000 test case for them it won't be able to
00:22:26.760 find them in the code and and change
00:22:28.200 them up
00:22:29.960 uh lastly uh it's quite expensive
00:22:32.000 actually uh the gem itself costs $90 per
00:22:34.720 seat per month although it is free for
00:22:36.240 open source and uh support and resources
00:22:39.279 out there can be Slims and non-existent
00:22:41.080 there aren't a lot of companies or or uh
00:22:43.120 coders really doing this as far as I
00:22:44.760 know uh but I would say this leaves some
00:22:47.279 opportunity for someone to go experiment
00:22:49.240 write some blog posts uh you can you can
00:22:51.440 be the face of of mutant for the
00:22:54.559 future so the question then uh we end
00:22:57.440 with is kind of is this for your code
00:22:59.159 base I think I would very much hesitate
00:23:01.600 to inject uh mutation testing into like
00:23:04.159 a legacy rails monolith that has lots
00:23:06.679 and lots of models and classes um if I
00:23:09.240 was starting something new like a small
00:23:11.039 micr service to the side of that or or
00:23:13.400 even just a new project all together I
00:23:15.400 think it would be a good thing to add
00:23:16.640 especially if you want uh to have you
00:23:19.480 know if you want your API or your your
00:23:21.400 service to have really rock solid uh
00:23:23.840 dependability and and error free uh
00:23:26.559 confusion free uh eror report and
00:23:28.720 everything like that so um but yeah
00:23:32.200 again like I say I would I would
00:23:33.120 hesitate to add to an existing codebase
00:23:35.480 except for maybe incrementally you might
00:23:36.919 start with just your model files or
00:23:38.919 perhaps some uh some lib classes that
00:23:41.039 you know are smaller and easier to
00:23:45.559 test all right here's uh my references
00:23:47.919 you guys can go play with the app at
00:23:49.360 this address I left the uh GitHub repo
00:23:52.240 on there as well for
00:23:54.000 you and I just wanted to stop end with a
00:23:56.480 final word I had a very difficult week
00:23:58.919 last week but I found this quotee by
00:24:00.360 Professor X that inspired me just
00:24:02.679 because someone stumbles and loses their
00:24:04.240 path doesn't mean they're lost forever
00:24:06.080 sometimes we all need a little help uh
00:24:08.440 it resonated with me and I I hope it
00:24:10.000 resonates with you
00:24:11.679 too that's all for me thank you
Explore all talks recorded at RubyConf 2024
+64