Index: [Article Count Order] [Thread]

Date:  Mon, 18 Oct 2004 12:53:15 +0900
From:  katayama@....jp
Subject:  [XP-jp:05087] Re: DbC on Java/C#...
To:  extremeprogramming-jp@....jp
Message-Id:  <JM200410181253152.264161984@....jp>
In-Reply-To:  <20041018013112.FAE9.KITO@....jp>
References:  <200410150511.i9F5BmN27653@....jp>	<20041018013112.FAE9.KITO@....jp>
X-Mail-Count: 05087

片山です。こんにちは。

とりあえずテストインターフェース生成を作ってみました。

Targetクラスが、テスト対象クラスになります。

TestInterfaceGeneratorにソース出力パスとテストインターフェースを
作りたいclass(この場合だとTarget.class)を渡すと、メソッドとフィールドを見て
同名のインターフェースを生成します。

実際のテスト時は、TestInvokeクラスに書いてあるように、実インスタンスと
Proxyインスタンスを生成します。この手順はみやむこさんの方法と同一です。

Target target = new Target();
TargetTestInterface testInterface = 
            (TargetTestInterface)PrivateProxy.
              createProxy(target,TargetTestInterface.class);

テスト時のメソッドに関しては、みやむこさんのPrivateProxyと呼び出し方は同一です。
フィールドに関してはテストインターフェース生成時に
「setフィールド名+_TestInvoke」と「getフィールド名+_TestInvoke」と
いうメソッドを生成しますので、それを使って呼び出しを行います。

例えば、privateFieldというメンバ変数に対してはsetPrivateField_TestInvocation
というメソッドが定義されるので、これを使って
        testInterface.setPrivateField_TestInvocation(5);
とテスト時にセットします。

PrivateProxy内部で「_TestInvoke」と「set,get」をキーにしてメソッドを調べ、
もし合致していればフィールドに対して直接アクセスを行います。

TestInterfaceGeneratorはとりあえず実装ですが、Antタスクに入れたりすれば
自動化は容易と思います。


---Target

package testinterface;

import java.io.IOException;

public class Target{
    protected String protectedField = "protected";
    private int privateField = 100;
    
    private int calc(int num){
        return num*2;
    }
    protected String append(String name1,String name2){
        return name1+ protectedField + name2;
    } 
    private void exception() throws IOException,IllegalArgumentException{
    }
}

---TestInterfaceGenerator

package testinterface;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestInterfaceGenerator{
    
    public void generate(String generateDir,Class targetClass) 
            throws IOException{
        
        String packageName = targetClass.getPackage().getName()+".test";
        String targetClassName =
             GeneratorUtils.getClassName(targetClass.getName());
        String interfaceName = targetClassName+"TestInterface";
        
        String dirPath = generateDir + "/" + packageName.replace('.','/');
        File javaFileDir = new File(dirPath);
        if(!javaFileDir.exists())
            javaFileDir.mkdirs();
            
        String javaFilePath = dirPath+"/"+interfaceName;
        File javaFile = new File(javaFilePath+".java");
        if(!javaFile.exists())
            javaFile.createNewFile();
        
        PrintWriter writer = new PrintWriter(new FileOutputStream(javaFile));
        
        writer.println("package " + packageName + ";");
        writer.println("public interface " + interfaceName);
        writer.println("{");
        
        Field[] fields = targetClass.getDeclaredFields();
        for(int i = 0;i < fields.length;i++){
            Field field = fields[i];
            String fieldName = field.getName();
            String methodName = GeneratorUtils.startCharToUpperCase(fieldName);
            Class type = field.getType();
            
            writer.println("\t"+"void set" + methodName 
            + GeneratorConst.FIELDINVOKE_IDENTIFIRE+"(" + type.getName() + " "
            + fieldName + ");");
            writer.println("\t"+type.getName() + " get" + methodName
             + GeneratorConst.FIELDINVOKE_IDENTIFIRE+"();");
        }
        
        Method[] methods = targetClass.getDeclaredMethods();
        for(int i = 0;i < methods.length;i++){
            Method method = methods[i];
            String methodName = method.getName();
            Class returnType = method.getReturnType();
            Class[] paramTypes = method.getParameterTypes();
            Class[] exceptionTypes = method.getExceptionTypes();
            
            writer.print("\t"+returnType.getName() + " " + methodName + "(");
            for(int j = 0;j < paramTypes.length;j++){
                if(j != 0)
                    writer.print(",");
                writer.print(paramTypes[j].getName() + " param" + j);
            }
            writer.print(")");

            for(int j = 0;j < exceptionTypes.length;j++){
                if(j == 0)
                    writer.print(" throws ");
                else
                    writer.print(",");
                writer.print(exceptionTypes[j].getName());
            }
            writer.println(";");
        }
        writer.println();
        writer.println("}");
        writer.flush();
        writer.close();     
    }
}


----PrivateProxy

package testinterface;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public final class PrivateProxy{
    
    public static Object createProxy(final Object target, Class interfaceClass){
        
        InvocationHandler handler = new InvocationHandler(){
            public Object invoke(Object proxy, Method method, Object[] args) 
                throws Throwable{                               
                if(method.getName().endsWith(
                    GeneratorConst.FIELDINVOKE_IDENTIFIRE)){
                    String methodName = method.getName();
                    boolean set = methodName.startsWith("set");
                    String fieldName =
                     methodName.substring(3,methodName.lastIndexOf(
                    GeneratorConst.FIELDINVOKE_IDENTIFIRE));
                    fieldName = GeneratorUtils.startCharToLowerCase(fieldName);
                    
                    Field targetField =
                         target.getClass().getDeclaredField(fieldName);
                    targetField.setAccessible(true);
                    if(set){
                        targetField.set(target,args[0]);
                        return null;
                    }                       
                    else{
                        return targetField.get(target);
                    }                       
                }
                else{                   
                    Method targetMethod = 
                        target.getClass()
                        .getDeclaredMethod(method.getName(),
                            method.getParameterTypes());
                    targetMethod.setAccessible(true);
                    return targetMethod.invoke(target,args);
                }
            }
        };

        return Proxy.newProxyInstance(
            interfaceClass.getClassLoader(),
            new Class[] { interfaceClass },handler);
    }
}

--GeneratorConst


package testinterface;

public interface GeneratorConst{
    public static final String FIELDINVOKE_IDENTIFIRE = "_TestInvocation";
}

--GeneratorUtils

package testinterface;

public class GeneratorUtils{
    
    public static String startCharToUpperCase(String text){
        return text.substring(0,1).toUpperCase() 
                + text.substring(1,text.length());
    }
    public static String startCharToLowerCase(String text){
        return text.substring(0,1).toLowerCase() 
                + text.substring(1,text.length());
    }
    public static String getClassName(String fqdn){
        return fqdn.substring(fqdn.lastIndexOf(".")+1,fqdn.length());       
    }       
}

----InvokeTest

package testinterface;

import testinterface.test.TargetTestInterface;

public class InvokeTest{

    public static void main(String[] args)
    {
        Target target = new Target();
        TargetTestInterface testInterface = 
            (TargetTestInterface)PrivateProxy
                .createProxy(target,TargetTestInterface.class);
        
        System.out.println(testInterface.append("aa","bb"));
        System.out.println(testInterface.calc(10));
        testInterface.setPrivateField_TestInvocation(5);
        System.out.println(testInterface.getPrivateField_TestInvocation());
    }
}



-------------------------------------------
株式会社キャピタル・アセット・プランニング
システム開発部 
片山 暁雄
mail:katayama@....jp
Tel:03-3256-1570
Fax:03-5296-9911
-------------------------------------------