Seasar DI Container with AOP

S2Unit.NETでテストを楽しく簡単に

S2Container.NET(バージョン1.1以降)では、コンテナを使った開発のテストを楽しくおこなえるようにテスティングフレームワークが組み込まれています。 MbUnitを拡張しています。主な機能は以下のとおりです。

  • S2Containerの自動作成
    • テストメソッド(TestXxx)ごとに自動的にS2Containerを作成します。
    • S2Containerに対するRegister(),GetComponent(),Include()が用意されています。
    • Include()するdiconファイルのPATHがテストクラスと同じ名前空間にある場合は、名前空間部分のパスは省略できます。
  • フィールドへの自動バインディング
    • Testクラスにstatic,readonly,finalのいずれでもないフィールドがあり、 その名前からアンダースコア(_)を(先頭もしくは後尾から)除いた名前のコンポーネントがコンテナに存在すれば自動的にセットされます。
    • Testクラスのフィールドに、代入することのできるコンポーネント(参照型かDateTime)がコンテナに存在すれば、S2Containerから取り出して自動的にセットされます。
    • テストメソッドが終わると、自動セットされた値は自動的にnull(VBの場合はNothing)がセットされます。
  • テストメソッド毎の初期化・終了処理
    • テストメソッド(TestXxx)に対応するSetUpXxx(),TearDownXxx()を定義しておくと、 SetUp()の後、TearDown()の前に自動的に呼び出されます。 個別のテストメソッドごとの初期化・終了処理を簡単に行えるようになります。
  • トランザクションの自動制御
    • テストメソッドのS2属性にTx列挙型(Seasar.Extension.Unit.Tx)のRollback(列挙子)を指定すると、 テストメソッドの直前にトランザクションを開始し、テストメソッドの直後にトランザクションをロールバックするので、 データベースに関するテストを行った場合のクリーンアップの処理が不要になります。

事前の準備

S2Unit.NETではMbUnit 2.3.0.0を使用しています。 事前にMbUnitをインストールしておく必要があります。

テストクラスはSeasar.Extension.Unit.S2TestCaseクラスを継承します。 S2TestCaseクラスはSeasar.Unit.dllに含まれてるので、テストコードを含むプロジェクトではSeasar.Unit.dllを参照に追加して下さい。

S2Containerの自動作成

テストクラスはSeasar.Extension.Unit.S2TestCaseを継承して作成します。 クラスにはもちろんTestFixture属性(MbUnit.Framework.TestFixtureAttribute)も定義します。 テストメソッドにはTest属性(MbUnit.Framework.TestAttribute)と共に、 S2属性(Seasar.Extension.Unit.S2Attribute)を定義します。 このようなテストクラスを準備するとS2Containerが自動的に作成されます。

C#
[TestFixture]
public class HogeTest : S2TestCase
{
    [Test, S2]
    public void TestHogeHoge()
    {
        // テストコードを書きます
    }
}
VB.NET
<TestFixture()> _
Public Class HogeTest
   Inherits S2TestCase
   
    <Test(), S2()> _
    Public sub TestHogeHoge()
        ' テストコードを書きます
    End Sub
    
End Class

S2TestClassにはS2Container.NETに対応するRegister(), GetComponent(), Include() が用意されています。

C#
public void TestHogeHoge()
{
    // diconファイルを取り込みます
    Include("Hoge.dicon");
    
    // S2Containerにコンポーネントを登録します
    Register(typeof(Hashtable));
    
    // S2Containerからコンポーネントを取得します
    Hashtable table = (Hashtable) GetComponent(typeof(Hashtable));
}
VB.NET
<Test(), S2()> _
Public sub TestHogeHoge()

    ' diconファイルを取り込みます
    Include("Hoge.dicon")
    
    ' S2Containerにコンポーネントを登録します
    Register(typeof(Hashtable))
    
    ' S2Containerからコンポーネントを取得します
    Dim table As Hashtable = CType(GetComponent(GetType(Hashtable)), Hashtable)
    
    ' 明示的にキャストしない場合は以下でも可
    ' Dim table As Hashtable = GetComponent(GetType(Hashtable))

End Sub

Include()するdiconファイルのPATHがテストクラスと同じ名前空間にある場合は、名前空間部分のパスは省略できます。 ファイルシステムでdiconファイルのPATHを指定する場合は、アセンブリと同じ位置の置くと相対パスとなり省略することができます。 (ただしビルドイベント等でアセンブリと同じフォルダにはき出すなどの処理が必要です)

Foo.Fuga名前空間にTest.diconを用意している場合、 Foo.Fuga名前空間にあるクラスでは以下のようにdiconファイルのパスを省略することが出来ます。

C#
namespace Foo.Fuga
{
    [TestFixture]
    public class HogeTest : S2TestCase
    {
        [Test, S2]
        public void TestHogeHoge()
        {
            Include("Test.dicon");
        }
    }
}
VB.NET
Namespace Foo.Fuga
   <TestFixture()> _
   Public Class HogeTest
      Inherits S2TestCase
      
      <Test(), S2()> _
      Public Sub TestHogeHoge()
         Include("Test.dicon")
      End Sub
      
   End Class
End Namespace

フィールドへの自動バインディング

Testクラスにstatic,readonly,finalのいずれでもないフィールドがあり、 その名前からアンダースコア(_)を(先頭もしくは後尾から)除いた名前のコンポーネントがコンテナに存在すれば自動的にセットされます。

Test.dicon
<components>
    <component name="abc">"hoge"</component>
</components>
C#
[TestFixture]
public class HogeTest : S2TestCase
{
    private string _abc = null;

    [Test, S2]
    public void TestHogeHoge()
    {
        Include("Test.dicon");
        Assert.AreEqual("hoge", _abc);
    }
}
VB.NET
<TestFixture()> _
Public Class HogeTest
   Inherits S2TestCase
   
    Private _abc As String = Nothing
   
    <Test(), S2()> _
    Public Sub TestHogeHoge()
        Include("Test.dicon")
        Assert.AreEqual("hoge", _abc)
    End Sub

End Class

Testクラスのフィールドに、代入することのできるコンポーネント(参照型かDateTime)がコンテナに存在すれば、S2Containerから取り出して自動的にセットされます。

Test.dicon
<components>
    <component class="System.Collections.ArrayList" />
</components>
C#
[TestFixture]
public class HogeTest : S2TestCase
{
    private IList _list = null;

    [Test, S2]
    public void TestHogeHoge()
    {
        Include("Test.dicon");
        Assert.IsNotNull(_list);
    }
}
VB.NET
<TestFixture()>  _
Public Class HogeTest
    Inherits S2TestCase

    Private _list As IList = Nothing

    <Test(), S2()> _
    Public Sub TestHogeHoge()
        Include("Test.dicon")
        Assert.IsNotNull(_list)
    End Sub

End Class

テストメソッドが終わると、自動セットされた値は自動的にnull(VBの場合はNothing)がセットされます。

テストメソッド毎の初期化・終了処理

テストメソッド(TestXxx)に対応するSetUpXxx(),TearDownXxx()を定義しておくと、 SetUp()の後、TearDown()の前に自動的に呼び出されます。 個別のテストメソッドごとの初期化・終了処理を簡単に行えるようになります。

C#
[TestFixture]
public class HogeTest : S2TestCase
{
    public void SetUpHogeHoge() 
    {
        // 初期化処理を行います 
    }

    [Test, S2]
    public void TestHogeHoge()
    {
        // テストコードを書きます
    }

    public void TearDownHogeHoge()
    {
        // 終了処理を書きます
    }
}
VB.NET
<TestFixture()> _
Public Class HogeTest
    Inherits S2TestCase
   
    Public Sub SetUpHogeHoge()
        ' 初期化処理を行います
    End Sub 

    <Test(), S2()> _
    Public Sub TestHogeHoge()
        ' テストコードを書きます
    End Sub

    Public Sub TearDownHogeHoge()
        ' 終了処理を書きます
    End Sub

End Class

トランザクションの自動制御

テストメソッドのS2属性にTx列挙型(Seasar.Extension.Unit.Tx)のRollback(列挙子)を指定すると、 テストメソッドの直前にトランザクションを開始し、テストメソッドの直後にトランザクションをロールバックするので、 データベースに関するテストを行った場合のクリーンアップの処理が不要になります。

Rollbackの他に、Tx列挙型のCommit(列挙子)を指定すると、自動的にトランザクションをコミットします。 NotSupported(列挙子)を指定すると、トランザクションは開始しません。 S2属性にTx列挙型の列挙子を与えない場合は、デフォルトでNotSupported(列挙子)を指定したのと同じ動作をします。

またトランザクションを扱うためには、下記のようにTransactionContextとDataSourceを定義したdiconファイルを 読み込んでおく必要があります。

Ado.dicon
<components namespace="Ado">

    <component name="SqlClient" class="Seasar.Extension.ADO.DataProvider">
        <property name="ConnectionType">"System.Data.SqlClient.SqlConnection"</property>
        <property name="CommandType">"System.Data.SqlClient.SqlCommand"</property>
        <property name="ParameterType">"System.Data.SqlClient.SqlParameter"</property>
        <property name="DataAdapterType">"System.Data.SqlClient.SqlDataAdapter"</property>
    </component>
    
    <component name="DataSource" class="Seasar.Extension.Tx.Impl.TxDataSource">
        <property name="DataProvider">SqlClient</property>
        <property name="ConnectionString">
            "Server=サーバ名;database=s2dotnetdemo;Integrated Security=SSPI"
       </property>
    </component>

    <component class="Seasar.Extension.Tx.Impl.TransactionContext" />
    
</components>
C#
[TestFixture]
public class HogeTest : S2TestCase
{
    public void SetUpHogeHoge() 
    {
        Include("Ado.dicon");
    }

    [Test, S2(Tx.Rollback)]
    public void TestHogeHoge()
    {
        // メソッド終了後に自動的にロールバックされます
    }
}
VB.NET
<TestFixture> _ 
Public Class HogeTest
     Inherits S2TestCase

    Public  Sub SetUpHogeHoge()
        Include("Ado.dicon")
    End Sub
 
    <Test, S2(Tx.Rollback)> _ 
    Public  Sub TestHogeHoge()
        ' メソッド終了後に自動的にロールバックされます
    End Sub

End Class