0

I am building a program that recognizes test classes and gives the coder an idea how much test code he wrote compared to the actual source code. My question is how can I make the program recognize a test class despite the type of tests that has been used? what is the main sign that you may find in a test class? Is it the @Test notation? If so how to handle it?

This is a piece of code of a class that I made to recognize test classes:

public void walk(String path) throws FileNotFoundException {
    int countFiles = 0;
    File root = new File(path);
    File[] list = root.listFiles();
    if (list == null) {
        return;
    }

    for (File f : list) {
        if (f.isDirectory()) {
            walk(f.getAbsolutePath());
        }

        if (f.getName().endsWith(".java")) {

            System.out.println("File:" + f.getName());
               Scanner sc2 = new Scanner(f);

            while (sc2.hasNextLine()) {

                count++;

                String str = sc2.nextLine();

                for (int i = 0; i < str.length(); i++) {
                    if(str.equals("@Test")){
                    System.out.println("this a test clsee!");
                }

But unfortunately my idea is not working. So what can I do?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Why is your idea not working? – Idos Feb 27 '16 at 14:01
  • I dont know am getting no result even that I have several test classes, either the if statement is placed wrong or that the condition is not the best ? (str.equals("@Test")); – Maja Hansson Feb 27 '16 at 14:04
  • *I don't know* is rarely a good answer. Please provide an [mcve](http://stackoverflow.com/help/mcve) if you wish to get a meaningful answer to your question. – Idos Feb 27 '16 at 14:06
  • I think I have already provided that in the question, you may need to read the question one more time! – Maja Hansson Feb 27 '16 at 14:09
  • Read the link content, please. – Idos Feb 27 '16 at 14:09
  • I read it long time ago, am not a beginner on stackoverflow, in addition to all of that am a thesis student, if you have a real answer feel free to provide it! Otherwise, you propably have other things to take care of, so do it! – Maja Hansson Feb 27 '16 at 14:13
  • Most of the time, test source files are not in the same directory as production source files (Maven/gradle use src/main/java and src/test/java). Not all classes in the test directory are unit tests, but it's still code that is written to support tests (utility classes, etc.). Why don't you simply rely on directories, and let the user specify the directories where production code is, and the directories where test code is? – JB Nizet Feb 27 '16 at 14:15
  • my advice here would be to look into the concept of "Code Coverage" There are a lot of tools out there built into your build tools to produce code coverage reports that tell you how much of your code is tested. These tools produce charts and reports. y – bsautner Feb 27 '16 at 14:15
  • Don't know if this helps, but you can do the same without java (2 lines of bash code): `find -name *.java | xargs -I{} wc -l {} | awk '{s+=$1}END{print s}'` - all the lines and `find -name *Test.java | xargs -I{} wc -l {} | awk '{s+=$1}END{print s}'` - number of test lines. – Stanislav Bashkyrtsev Feb 27 '16 at 16:56

3 Answers3

1

I realize this might not be exactly the answer you are looking for, but let me give you two alternative ideas nonetheless.

  1. If you are using maven or gradle as your build tool, you should have all your test classes in the src/test/java folder anyway, making it easy to distinguish them from your production code (located in the src/main/java folder). Other tools might have similar structures. If yours doesn't you might want to consider switching.

    Once you have your test code and your productive code in different folders, you can simply count all *.java files within those folder structures (or write a bat/bash/whatever script to do it for you).

  2. Since the pure amount of test code doesn't really tell you anything about your actual test coverage, you might want to look for a tool that helps you measure this coverage and focus on the quality rather than the quantity of your tests.

    If you are using Eclipse, there is e.g. EclEmma (which is maybe not the only one and not the best one but simply the one I am using). It adds a new launch mode to Eclipse and you can then start your unit tests in Coverage mode. In this mode, EclEmma records which lines of your production code are being executed when your test runs, giving you a good idea of which parts of your code are tested and which are not. It also computes some statistics regarding the percentage of lines or branches covered to help you identify weak spots.

(Now to at least quickly address your code: In theory your approach should work. In practice it looks like you should read up on string comparison in java. It might be worth a shot to replace your str.equals("@Test") loop with a simple str.contains("@Test").)

Marvin
  • 13,325
  • 3
  • 51
  • 57
  • Your answer is more than appreciated! But also having 100% test coverage wont gurantee a bug free program ? right ?:) that is why am trying to make this in my research! one of my research question is how much test code is enough for 5000 lines program written by two developers? and i will try to find answer by analyzing an amount of open source programs source code and test code ! I really like your answer feel free to come up with more ideas ! – Maja Hansson Feb 27 '16 at 14:34
  • Right. a) 100% test coverage doesn't tell you that all _combinations_ of branches work correctly and b) once your logic gets more complex the chances increase that your test code is either incorrect or just tests nonsense. Not sure if asking "how much test code is enough for lines of code" really covers that topic well enough, though. – Marvin Feb 27 '16 at 14:48
0
  1. Don't use str.equals("@Test"), use str.contains("@Test")
  2. Also good if you check str.contains("org.testng"), str.contains("org.junit"), str.contains("org.easymock"), str.contains("org.mockito") and so on.

Upd: For example, instead of code:

Scanner sc2 = new Scanner(f);

while (sc2.hasNextLine()) {

    count++;

    String str = sc2.nextLine();

    for (int i = 0; i < str.length(); i++) {
        if(str.equals("@Test")){
        System.out.println("this a test clsee!");

You should use code:

   Scanner sc2 = new Scanner(f);
   boolean flag = false;
   while (sc2.hasNextLine() && !flag) {
        String str = sc2.nextLine();
        if(str.contains("@Test")){
            flag = true;
        }
  }
  if(flag) {
        System.out.println("this a test clsee!");
  } 
Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59
  • Hi! Your Idea works, but there is a small problem, now iam having a small problem. As Am using this code now if(str.contains("@Test")){ System.out.println("this is a test class"); } lets say that a class has 4 test methods in other words if you have 4 @Test its printing four times! what can I do to make it print only one time? – Maja Hansson Feb 28 '16 at 22:31
0

I not sure if this is what you are looking for. After you get file name follow below steps.

Classfile.class.getMethods()

Iterate through methods array and check for annotation class

method.getAnnotation(Test.class)

As suggested by others it's always a better practice not to club test classes with businesses code.

I have added full code snippet here.

public class ClassFinder {

private static final char PKG_SEPARATOR = '.';

private static final char DIR_SEPARATOR = '/';

private static final String CLASS_FILE_SUFFIX = ".class";

private static final String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the package '%s' exists?";

public static List<Class<?>> find(String scannedPackage) {
    String scannedPath = scannedPackage.replace(PKG_SEPARATOR, DIR_SEPARATOR);
    URL scannedUrl = Thread.currentThread().getContextClassLoader().getResource(scannedPath);
    if (scannedUrl == null) {
        throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage));
    }
    File scannedDir = new File(scannedUrl.getFile());
    List<Class<?>> classes = new ArrayList<Class<?>>();
    for (File file : scannedDir.listFiles()) {
        classes.addAll(find(file, scannedPackage));
    }
    return classes;
}

private static List<Class<?>> find(File file, String scannedPackage) {
    List<Class<?>> classes = new ArrayList<Class<?>>();
    String resource = scannedPackage + PKG_SEPARATOR + file.getName();
    if (file.isDirectory()) {
        for (File child : file.listFiles()) {
            classes.addAll(find(child, resource));
        }
    } else if (resource.endsWith(CLASS_FILE_SUFFIX)) {
        int endIndex = resource.length() - CLASS_FILE_SUFFIX.length();
        String className = resource.substring(0, endIndex);
        try {
            classes.add(Class.forName(className));
        } catch (ClassNotFoundException ignore) {
        }
    }
    return classes;
}

public static void main(String[] args) {
    List<Class<?>> classes = ClassFinder.find("com.vijay.junitapp4");
    Method[] methods = null;
    for (Class<?> class1 : classes) {
        methods = class1.getMethods();
        Test test = null;
        for (int j = 0; j < methods.length; j++) {
            test = methods[j].getAnnotation(Test.class);
            if(null != test){
                System.out.println("we found a teat case");
                break;
            }
        }
    }
}

}

Vijay k
  • 5
  • 7
  • Am not really sure If i get your Idea, may please describe more ? – Maja Hansson Feb 28 '16 at 22:49
  • The file names you get are strings, hence to convert it to class file i need to use Class.forName(""). I am providing the code snippet which i followed from http://stackoverflow.com/questions/15519626/how-to-get-all-classes-names-in-a-package Thanks to author. – Vijay k Feb 29 '16 at 01:37