0

基本クラスに何千もの派生クラスがある大規模なプロジェクトに取り組んでいます (複数の開発者がそれらに取り組んでいます)。各クラスは、一連のメソッドをオーバーライドすることが期待されています。私は最初に、受け入れ可能なパターンに準拠したコード テンプレートを使用して、これらの数千のクラス ファイルを生成しました。私は現在、開発者がこのパターンから逸脱していないことを確認するために単体テストを作成しています。生成されたクラスのサンプルを次に示します。

// Base class.
public abstract partial class BaseClass
{
    protected abstract bool OnTest ();
}

// Derived class. DO NOT CHANGE THE CLASS NAME!
public sealed partial class DerivedClass_00000001: BaseClass
{
    /// <summary>
    /// Do not modify the code template in any way.
    /// Write code only in the try and finally blocks in this method.
    /// </summary>
    protected override void OnTest ()
    {
        bool result = false;
        ComObject com = null;
        // Declare ALL value and reference type variables here. NOWHERE ELSE!
        // Variables that would otherwise be narrowly scoped should also be declared here.
        // Initialize all reference types to [null]. [object o;] does not conform. [object o = null;] conforms.
        // Initialize all value types to their default values. [int x;] does not conform. [int x = 0;] conforms.

        try
        {
            com = new ComObject();

            // Process COM objects here.
            // Do NOT return out of this function yourself!
        }
        finally
        {
            // Release all COM objects.
            System.Runtime.InteropServices.Marshal.ReleaseComObject(com);

            // Set all COM objects to [null].
            // The base class will take care of explicit garbage collection.
            com = null;
        }

        return (result);
    }
}

単体テストでは、リフレクションを介して次のことを確認できました。

  • このクラスは [BaseClass] から派生し、インターフェイスを実装していません。
  • クラス名はパターンに準拠しています。
  • catch ブロックはフィルター処理されていません。
  • 他の catch ブロックは追加されていません。
  • クラス レベルのフィールドまたはプロパティが宣言されていません。
  • すべてのメソッド値型変数は、宣言時に手動で初期化されています。
  • 派生クラスには他のメソッドは追加されていません。

上記はリフレクションを介して簡単に実現できますが、次のリストを主張するのに苦労しています。

  • catch ブロックは、キャッチした例外をラップしたり他の例外をスローしたりするのではなく、再スローします。
  • 最後の[return (result);]行は変更されておらず、他の[return (whatever);]呼び出しは追加されていません。これを達成する方法がわかりません。
  • IDisposable を実装するすべての参照型が破棄されていることを確認します。
  • [System.__ComObject] 型のすべての参照型が手動で逆参照され、finally ブロックで [null] に設定されていることを確認します。

ソースコードを解析することを考えましたが、絶対に必要でない限り、その解決策は好きではありません。これは厄介で、式ツリーがない限り、成功を保証することはほとんど不可能です。

ヒントをいただければ幸いです。

4

3 に答える 3

2

完全に自動化されたコード分析が本当に必要な場合は、ここでRoslyn CTPを使用してみてください。リフレクションよりも高度な構文およびセマンティクス分析を備えています。しかし、それはまだ多くの作業です。コードではなく、開発者と直接作業し、テンプレートやガイドラインを準備する方が、時間効率がよい場合があります。

于 2013-05-13T16:10:04.190 に答える
0

このような厳格な要件には十分な理由があると思いますが、代わりにラムダ/デリゲート/アクションをテスト関数に渡すことを検討しましたか?

すべてを解決することはできませんが、必要な動作のいくつかをより論理的に提供します (たとえば、返せない、クラス レベルの変数を持つことができない、指定された場所以外にコードを記述できない)。

それに関する最大の懸念は、キャプチャされた変数です...しかし、それには回避策があるかもしれません。

コード例:

//I'd make a few signatures....
bool OnTest<T1, T2> (Action<ComObject, T1, T2> logic, T1 first, T2 second)
    {
        bool result = false;
        ComObject com = null;

        //no checks needed re parameters
        //Can add reflection tests here if wanted before code is run.

        try
        {
            com = new ComObject();
            //can't return
            logic(com, first,second);
        }
        finally
        {
            // Release all COM objects.
            System.Runtime.InteropServices.Marshal.ReleaseComObject(com);

            // Set all COM objects to [null].
            // The base class will take care of explicit garbage collection.
            com = null;

            //If you want, we can check each argument and if it is disposable dispose.
            if (first is IDisposable  && first != null) ((IDisposable) first).Dispose();
            ...
        }

        return (result);  //can't be changed
    }

これがうまくいくかどうかはわかりませんが、それは単なる考えです。ああ、考えてみれば、それは完全ではなく、テストもされていません - 私はあなたがそれを大幅に開発することを期待しています.

于 2013-05-13T15:07:19.520 に答える