Spring Test Writer
One fine Saturday evening as I was eating my Mee Hoon Kuay at Old Airport Road Food Centre, I randomly recalled the 1195 lines of uncovered test cases for which I had to write test cases for in my current sprint.
Then, a brilliant idea came to mind (if I say so myself, lol).
Write a script to scan for java classes and generate test files and methods. A bonus if the automatically generated test cases could run and provide 100% coverage without any correction.
How To Run
Virtualenv
$ virtualenv env
$ source env/bin/activate
Pip
$ pip install -r requirements.txt
Run
$ python generate.py --src /home/userid/projects/helloworld/src
Components
- FileScanner: Scans for
.java
files from a given directory - ClassFileParser: Parses
.java
file content into aClassFile
Object - TestFileGenerator: Generates test file in
.java
format from aClassFile
object
FileScanner
Recursively scan for .java
files from a given root directory and return the list of class files.
ClassFileParser
Reads and parses the content of a class file into a ClassFile
object.
- Read content of
.java
file - Identify package
- Identify class name
- Scan for member variables
- Scan for methods
Identify package
package
code must be the first non-empty line in content- Line begins with the keyword
package
, followed by the package path and ends with a semicolon
Identify class name
Find the first line that follows the syntax:
[<access_modifier> ][<other_keywords> ]class <class_name>[ <extends_or_implements_class>][ {]
e.g.
public class HelloWorld {
public abstract class Hello {
public interface HelloInterface {
public class HelloWorld extends Hello {
public class HelloWorld implements HelloInterface {
public class HelloWorld extends Hello implements HelloInterface {
public static class HelloWorld<Hello, World> {
Extraction steps:
- Replace string
,
with,
- Split string by consecutive spaces into a list
- The element after the
class
element should be the class name
Additional Notes:
- If the class is an
abstract
class or aninterface
, then the test file cannot be generated since it cannot be instantiated.
Scan for member variables
Member variables generally follow the syntax:
[<access_modifier> ][<other_keywords> ]<data_type> <variable_name>[ = <RHS>];
e.g.
Object object;
int i = 0;
private String name;
protected Map<String, Object> map = new HashMap<>();
public static final int JANUARY = 1;
Extraction steps:
- Replace string
,
with,
- Split string by consecutive spaces into a list
- Remove keywords from list:
public
,private
,protected
,static
,final
- First element should be the data type
- Second element should be the variable name. Trim trailing
;
from variable name
Scan for methods
Methods generally follow the syntax:
[<access_modifier> ][<other_keywords> ]<return_data_type> <method_name>([<parameters>])[;|( {)]
e.g.
void getName()
protected Person getByName(String firstName, String lastName) throws Exception
public abstract void getAddress();
private final int getDays()
public static final Map<String, Object> setName(String name)
Extraction steps:
- Replace string
,
with,
- Split string by consecutive spaces into a list
- Remove keywords from list:
public
,private
,protected
,static
,abstract
- First element should be the return_data_type
- Split second element by the first
(
- First element should be the method_name
- Trim second element by trailing
)
- Split remaining line by
,
- Split remaining line by
Additional Notes:
- If the method is
abstract
, then the test method cannot be generated since the method cannot be invoked.
TestFileGenerator
- Create test file
- Generate code
Create test file
TODO
Generate code
- Generate package code
- Generate imports
- Generate
@RunWith(SpringJUnit4ClassRunner.class)
annotation - Generate class
- Generate member variables
- Generate test methods
Generate Imports
- Assert methods
- Mockito methods and annotations
Generate Member Variables
- Declare test class variable
- If test class has member variables, then:
- Declare member variables of test class and annotate with ‘@Mock’
- Annotate test class variable with ‘@InjectMocks’
- Implement ‘initMocks()’ method and annotate with ‘@Before’
Generate Test Methods
- Scan for non-private methods and create a test method
- e.g. found 'public void getName()', so generate 'public void getNameTest()' method in test file
- Annotate test method with '@Test'