Fenriswolf 程式筆記

奮利斯狼的地盤,小綿羊勿入

JUnit狂想

在管理 developers 的 testcases 常會發現一件事
在加入新功能時常會忘了補 test methods
雖然可以用 coverage report 來確認
但無法看出一個問題,某個 method 沒有自己的 test method 但是有被別的 method call 到
如果 testcases 跟實作的 classes 可以 implement 同樣的 interfaces 是最好的

在 junit3 的 spec 要求 test methods 要以 void testXXX() 的格式來寫
junit4 雖然加入了 annotation @Test,但是仍然要求 method 必須是 void, no parameters
所以想達到我的要求就要改寫 JUnit Runner

以 JUnit4 為例
1. 建立 TestMethod extends org.junit.internal.runners.TestMethod 並改寫

public void invoke(Object test) throws IllegalArgumentException,
        IllegalAccessException, InvocationTargetException {
    Class<?>[] types = fMethod.getParameterTypes();
    Object[] params = new Object[types.length];

    for (int i = 0; i < types.length; i++) {
        if (types[i].isPrimitive()) {
            if ("boolean".equals(types[i].getSimpleName())) {
                params[i] = false;
            } else {
                params[i] = 0;
            }
        } else {
            params[i] = null;
        }
    }

    fMethod.invoke(test, params);
}

TestMethod 是決定要用什麼方式去 call testcases 的 methods

2. 建立 MethodValidator 並改寫

private void validateTestMethods(Class<? extends Annotation> annotation,
            boolean isStatic) {
    List<Method> methods = fTestClass.getAnnotatedMethods(annotation);

    for (Method each : methods) {
        if (Modifier.isStatic(each.getModifiers()) != isStatic) {
            String state = isStatic ? "should" : "should not";
            fErrors.add(new Exception("Method " + each.getName() + "() "
                    + state + " be static"));
        }
        if (!Modifier.isPublic(each.getDeclaringClass().getModifiers()))
            fErrors.add(new Exception("Class "
                    + each.getDeclaringClass().getName()
                    + " should be public"));
        if (!Modifier.isPublic(each.getModifiers()))
            fErrors.add(new Exception("Method " + each.getName()
                    + " should be public"));
//            if (each.getReturnType() != Void.TYPE)
//                fErrors.add(new Exception("Method " + each.getName()
//                        + " should be void"));
//            if (each.getParameterTypes().length != 0)
//                fErrors.add(new Exception("Method " + each.getName()
//                        + " should have no parameters"));
    }
}

MethodValidator 是在執行 testcases 之前所要做的 validation rules
mark 的部分是 void 及 no parameters 的檢查

3. 建立 ClassRunner extends org.junit.internal.runners.JUnit4ClassRunner 並取代舊有的 TestMethod 及 MethodValidator

protected void validate() throws InitializationError {
    MyMethodValidator methodValidator= new MyMethodValidator(getTestClass());
    methodValidator.validateMethodsForDefaultRunner();
    methodValidator.assertValid();
}

protected TestMethod wrapMethod(Method method) {
    return new FwTestMethod(method, getTestClass());
}

接下來就是寫自己的 testcase 並執行測試啦

@RunWith(FwClassRunner.class)
public class UnitTest implements IService {    
    protected IService service = new ServiceImpl();
    
    @Test
    public String method1() {
        String str = service.method1();
        
        assertEquals(str, "abc");
        
        return null;
    }

    @Test
    public void method2(boolean b, int i, String str) {
        service.method2(false, 1, "abc");
    }

    @Test
    public List method3() {
        List list = service.method3();
        
        assertTrue(list.size() > 0);
        
        return null;
    }
}

最重要的地方就是把 default runner 用 @RunWith 換成自定的 Runner
test methods 的傳入值及回傳值是沒有用的,主要判斷還是 assertion
所以回傳值隨便給並不會影響測試
 
 
執行環境
JDK 1.6.0_03
JUnit 4.4

參考資料
junit.org

程式下載
FwClassRunner.java
FwMethodValidator.java
FwTestMethod.java

廣告

2012/03/19 - Posted by | Java Tool |

仍無迴響。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

%d 位部落客按了讚: