An Automatic Refactoring Framework for Replacing
Test-Production Inheritance by Mocking Mechanism
Mock Syntax Variations
In Section 5.1 Auto-Refactoring Procedure, we mentioned that there are several syntax variations for the mocking instance creations and method stubbings. We summarized the variations and the corresponding use scenarios here.
Spy Concrete Class
As shown in Figure 2, we use spy to create mocking object when productionClass is a concrete class. Spy creates a real object and all methods defined in productionClass still behave in the same way as the normal instance.
Figure 2: Spy Concrete Class
Mock Interface
As shown in Figure 1, we use mock to create mocking object when productionClass is an interface without any default methods. The mocking object is merely a skeleton and the method remains unimplemented, that is, methods with return will return the default value (e.g., 0 for int, false for boolean, null for Object) and void methods will simply do nothing.
Figure 1: Mock Interface
Spy Real Instance
As shown in Figure 3, we create spy on a real instance when testSubclass invoked the non-default super constructor (line 2). This syntax ensure that the behavior of testSubclass and mocking object are consistent.
Spy Abstract Class
As shown in Figure 4, we use mock and withSettings() to create mocking object when productionClass is abstract and testSubclass invoked the non-default super constructor (line 2). We invoked the super constructor by calling useConstrucotr() method (line 14) when creating the mocking object. Since we use defaultAnswer(CALLS_REAL_METHODS), we actually create a mocking object that retain the original behavior with productionClass.
Figure 4: Spy Abstract Class
doAnswer
doAnswer entriely replaces the original method behavior, working similar to method overridden in inheritance. doAnswer were used on spying object to preserve the "overridden" behavior.
Figure 5: doAnswer Syntax
thenAnswer
thenAnswer adds additional actions to the stubbed method. It ensures tyep safe and more readable thus should be preferred whenever possible. We choose to use thenAnswer syntax when mocking production interfaces.
Figure 6: thenAnswer Syntax
doReturn
doReturn works similar with doAnswer which replaces the original method behavior. doReturn is used when the overridden method only contain a single return statement. We use doReturn when spying real objects since calling real methods on a spy object may bring side effects.
Figure 7: doReturn Syntax
thenReturn
thenReturn is used to stub method on mocking object when the overridden method only contain a single return statement.
Figure 8: thenReturn Syntax
doThrow
doThrow is used to stub method on spying object when the overridden method only contain a single throw statement.
Figure 9: doThrow Syntax
thenThrow
thenThrow is used to stub method on mocking object when the overridden method only contain a single throw statement.
Figure 10: thenThrow Syntax
doNothing
doNothing is used for setting stub methods to do nothing. It only used for spying object since methods on mocking object do nothing by default.
Figure 11: doNothing Syntax
Pseudo-Code: translateToMocking
In section 5.1.3, we use translateToMocking to preserve the references on the mock object. The reference to the testSubClass attribute/method is replaced to be the reference to the extracted attribute/variables or methods in testClass'. Figure 8 shows the pseudo-code of the translateToMocking, which processes each statement from codeBody in a while loop:
For the current statement, curStmt, the algorithm first checks whether it has reference to an attribute, TSC.attr, of testSubClass. If so, the reference to TSC.attr needs to be replaced properly (from line 2 to line 13). There are two different ways to replace the reference to TSC.attr, depending on its usage type introduced earlier. If TSC.attr is a checker/counter (line 3), and the curStmt is an assertion statement, we replace this assertion by MockVerify (line 4). If curStmt is not an assertion statement, it actually indicates an error, since checker/counter can only be referenced in an assertion statement by its definition (line 5). Otherwise, TSC.attr is a general attribute type (line 7), we just replace the reference to TSC.attr in curStmt by the reference to the local variable in the testCase (line 8) or the attribute in the testClass (line 10), depending on where the TSC.attr is extracted to be.
Figure 12. tanslateToMocking
Eclipse Plugin Link
As mentioned in Section 5.2 Implementation, we implemented the auto-refactoring framework as an Eclipse plugin. You can download the folder through this link: JMocker. You can install the plugin manually from local by providing it the folder.
Paper DataSets
Manual Refactoring Datasets
A detailed manual refactoring datasets for Table 1 in Section 3 Empirical Study. This table shows the distribution of classes filtered out by each filter.
Auto-Refactoring Evaluation Datasets
A detailed auto-refactoring datasets for Table 3 in Section 6 Evaluation. This table shows the distribution of classes filtered out by each filter.
Qualitative Evaluation
The raw survey data for Qualitative Evaluation in Section 6 Evaluation.