RevAjaxMutator
Automated Program Repair Tool for Ajax Web Applications
Motivating Example
We use Quizzy version 1.0
Under AGPL v.3 licence, source code of all mutated/faulty/repaired versions are available from: quizzy_src.zip
Usage Scenario
This usage scenario consists of two phases: before and after refactoring. Before refactoring, we use AjaxMutator to apply mutation analysis to Quizzy and improve their test case. In contrast, while refactoring, we make a common mistake which RevAjaxMutator automatically fixes.
Before Refactoring - Initial Test Cases
A running example is here.
Now we focus on the following code snippet where Quizzy has two click event handlers
on page elements identified by class names quizzy_quiz_lbl
and quizzy_quiz_opt
,
as shown in Figure below.
Once users click on these elements, Quizzy renders the corresponding description.
//add a click event to the radio buttons' label $('.quizzy_quiz_lbl').click(function () { //the user clicked on one of the options //get the id var thisId = $(this).attr('id'); //hack out the index and set selOpt to it var selQuiz = getSelQuiz(thisId); // modified for simplicity //make sure that the radio button is selected $('#quizzy_quiz_opt'+selQuiz).click(); }); //add another click event handler to the radio buttons $('.quizzy_quiz_opt').click(function() { //the user clicked on one of the options //get the id var thisId = $(this).attr('id'); //hack out the index and set selOpt to it var selQuiz = getSelQuiz(thisId); // modified for simplicity //slide up all other descriptions while sliding down the correct one $('.quizzy_quiz_desc[id!=quizzy_quiz_desc'+selQuiz+']').slideUp(slideSpeed, function() { $('#quizzy_quiz_desc' + selQuiz).slideDown(slideSpeed); }); });
For this functionality, we initially implement the following test cases by using JUnit and Selenium frameworks;
an @Test
represents a test case for JUnit.
The clickRadioButton
test case runs for clicking the radio button driver.findElement(By.id("quizzy_quiz_opt0"))
and confirming whether the rendered description driver.findElement(By.id("quizzy_quiz_desc0")).getText().trim()
conforms to the expected output "Several quizzes are available; min/max score range is 0-100."
.
As a difference between these test cases,
the ckickRadioButtonLabel
test case runs for clicking the label driver.findElement(By.id("quizzy_quiz_lbl0"))
.
Both these test cases covers all statements in this code snippet, passing on this initial version of Quizzy.
@Test public void clickRadioButton() { wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("quizzy_quiz_opt0"))); driver.findElement(By.id("quizzy_quiz_opt0")).click();; Assert.assertEquals("Several quizzes are available; min/max score range is 0-100.", driver.findElement(By.id("quizzy_quiz_desc0")).getText().trim()); } @Test public void ckickRadioButtonLabel() { wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("quizzy_quiz_lbl0"))); driver.findElement(By.id("quizzy_quiz_lbl0")).click(); Assert.assertEquals("Several quizzes are available; min/max score range is 0-100.", driver.findElement(By.id("quizzy_quiz_desc0")).getText().trim()); }
Before Refactoring - Mutation Testing
A running example is here.
Herein we apply mutation analysis to this version version.
For simplicity, we provide one of the mutations created by AjaxMutator below.
This mutation (i.e., injected fault) means that Quizzy invokes the callback function for the .quizzy_quiz_opt
element
even if users click on the .quizzy_quiz_lbl
element.
The figure below shows an erroneous behavior due to this injected fault.
@@ -48,11 +48,7 @@ - $('.quizzy_quiz_lbl').click(function () { - //the user clicked on one of the options - //get the id - var thisId = $(this).attr('id'); - - //hack out the index and set selOpt to it - var selQuiz = getSelQuiz(thisId); - - //make sure that the radio button is selected - $('#quizzy_quiz_opt'+selQuiz).click(); - }); + $('.quizzy_quiz_lbl').click(function() { + var thisId = $(this).attr('id'); + var selQuiz = getSelQuiz(thisId); + $('.quizzy_quiz_desc[id!=quizzy_quiz_desc' + selQuiz + ']').slideUp(slideSpeed, function() { + $('#quizzy_quiz_desc' + selQuiz).slideDown(slideSpeed); +}); +});
Although coverage of this initial test cases on the code snippet is 100%, they do not detect this injected fault.
Therefore, we improve the ckickRadioButtonLabel
test case
by adding the assertion statement below (containing the improvement
comment-out)
which detects the injected fault.
Finally, we obtain the improved test cases whose fault-detecting capability is higher that that of the initial test cases.
@Test public void clickRadioButton() { wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("quizzy_quiz_opt0"))); driver.findElement(By.id("quizzy_quiz_opt0")).click();; Assert.assertEquals("Several quizzes are available; min/max score range is 0-100.", driver.findElement(By.id("quizzy_quiz_desc0")).getText().trim()); } @Test public void ckickRadioButtonLabel() { wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("quizzy_quiz_lbl0"))); driver.findElement(By.id("quizzy_quiz_lbl0")).click(); Assert.assertEquals("Several quizzes are available; min/max score range is 0-100.", driver.findElement(By.id("quizzy_quiz_desc0")).getText().trim()); Assert.assertTrue(driver.findElement(By.id("quizzy_quiz_opt0")).isSelected()); // Improved }
After Refactoring - Actual Fault
A running example is here.
We then do refactoring; separately implementing callback functions for the radio-button and label elements.
While this refactoring, we consider to use input completion (e.g., that provided by Sublime-text)
and make make the following mistake of attaching a faulty callback function clickQuizzyQuizOpt
at the label element,
whereas a correct function is clickQuizzyLbl
. It then causes the same failure as that caused by mutation testing
so the improved test case can detect the made mistake.
//add a click event to the radio buttons' label $('.quizzy_quiz_lbl').click(clickQuizzyQuizOpt); // Fault //add another click event handler to the radio buttons $('.quizzy_quiz_opt').click(lickQuizzyQuizOpt); function clickQuizzyQuizOpt() {...} function clickQuizzyQuizLbl() {...}
After Refactoring - Automated Program Repair
A running example is here.
For the faulty version of Quizzy, we ran RevAjaxMutator. It then generated the following patch and all the test cases pass for the patched Quizzy. Thus, RevAjaxMutator is expected to automatically generate and validate Ajax applications might containing Ajax-related faults that AjaxMutator injects as common faults.
@@ -48 +48 @@ - $('.quizzy_quiz_lbl').click(clickQuizzyQuizOpt); + $('.quizzy_quiz_lbl').click(clickQuizzyQuizLbl);