| S2Container.NET TOP page # Easy Testing with S2Unit.NET S2Container.NET version 1.1 and higher includes testing framework to make testing much easier. The framework extends MbUnit and has the following features: Creation of S2Container S2Container is created for each test method (TestXxx). Register(), GetComponent(), and Include() methods are available. Path of a namespace may be omitted when the namespace of the dicon file PATH is the same as the test class namespace. Binding of fields If there is a field in a Test class that is not neither static, readonly, nor final and there exists a component with a name that equals the field name without the underscore (prefix or suffix), then that field and the component will be binded. If there is a component (reference datatype or DateTime) in a container that can be substituted, that component is retrieved from the S2Container and set. When the test method finishes, values that were set are reset to null (Nothing in VB). Initial and final processes per each test method If SetUpXxx() method and TearDownXxx() method is defined, they will be called after SetUp() method and before TearDown() method. Initialization and clean up steps for each test may be written in these methods. Transaction control If TxEnumerator(Seasar.Extension.Unit.Tx) is set to Rollback(Enumerator) in the S2 attribute in test method, transaction will marked to begin just before the test method and rolled back after test method finishes execution. This will allow tests to be repeated with the same set of database data. Testing with a database New DataSet may be retrieved by a primary key using Reload(DataSet) to reload data. Result from an execution may be compared with the expected result entered in Excel. Example: DataSet expected = ReadXls("expectedResult.xls"); S2Assert.AreEqual(expected, Reload(expected); Expected result of datatype DataSet may be compared to IList of IDictionary and IDictionary, object, IList of object by using the S2Assert.AreEqual() function. ## S2Unit.NET Installation Refer to S2Unit.NET Installation for details on installing S2Unit.NET. ## Creation of S2Container Test class is created by extending Seasar.Extension.Unit.S2TestCase. This class should define TestFixture attribute(MbUnit.Framework.TestFixtureAttribute). Test method should define Test attribute(MbUnit.Framework.TestAttribute) as well as S2 attribute(Seasar.Extension.Unit.S2Attribute). The test class will then create a S2Container. C#
    [TestFixture]
    public class HogeTest : S2TestCase
    {
        [Test, S2]
        public void TestHogeHoge()
        {
            // test codes
        }
    }
VB.NET
    <TestFixture()> _
    Public Class HogeTest
       Inherits S2TestCase

        <Test(), S2()> _
        Public sub TestHogeHoge()
            ' test codes
        End Sub

    End Class
S2TestClass has Register(), GetComponent(), and Include() methods that may be used with S2Container.NET. C#
    public void TestHogeHoge()
    {
        // read dicon file
        Include("Hoge.dicon");

        // Register components into a S2Container
        Register(typeof(Hashtable));

        // Retrieve components from a S2Container
        Hashtable table = (Hashtable) GetComponent(typeof(Hashtable));
    }
VB.NET
    <Test(), S2()> _
    Public sub TestHogeHoge()

        ' read dicon file
        Include("Hoge.dicon")

        ' Register components into a S2Container
        Register(typeof(Hashtable))

        ' Retrieve components from a S2Container
        Dim table As Hashtable = CType(GetComponent(GetType(Hashtable)), Hashtable)

        ' when there is no explicit cast, may be defined as below
        ' Dim table As Hashtable = GetComponent(GetType(Hashtable))

    End Sub
If the PATH of the dicon file is the same as the namespace of the test class, path in the namespace may be omittted. When PATH of the dicon file is specified in the file system, path will become relative path from the assembly. (However, process such as the buildin event, to output to the same folder as the assembly is necessary.) if Test.dicon is in the namespace Foo.Fuga, it is possible to omit dicon file path on classes that are in Foo.Fuga namespace. 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
## Binding of Fields If there is a field in a Test class that is not neither static, readonly, nor final and there exists a component with a name that equals the field name without the underscore (prefix or suffix), then that field and the component will be binded. 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
If there is a component (reference datatype or DateTime) in a container that can be substituted, that component is retrieved from the S2Container and set. 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
When the test method finishes, values that were set are reset to null (Nothing in VB). ## Initial and final processes per each test method If SetUpXxx() method and TearDownXxx() method is defined, they will be called after SetUp() method and before TearDown() method. Initialization and clean up steps for each test may be written in these methods. C#
    [TestFixture]
    public class HogeTest : S2TestCase
    {
        public void SetUpHogeHoge()
        {
            // initialization process
        }

        [Test, S2]
        public void TestHogeHoge()
        {
            // test codes
        }

        public void TearDownHogeHoge()
        {
            // final processing
        }
    }
VB.NET
    <TestFixture()> _
    Public Class HogeTest
        Inherits S2TestCase

        Public Sub SetUpHogeHoge()
            ' initialization process
        End Sub

        <Test(), S2()> _
        Public Sub TestHogeHoge()
            ' test codes
        End Sub

        Public Sub TearDownHogeHoge()
            ' final processing
        End Sub

    End Class
## Transaction control If TxEnumerator(Seasar.Extension.Unit.Tx) is set to Rollback(Enumerator) in the S2 attribute in test method, transaction will marked to begin just before the test method and rolled back after test method finishes execution. This will allow tests to be repeated with the same set of database data. If Commit(Enumerator) in TxEnumerator is specified beside Rollback, transaction will be committed. If NotSupported(Enumerator) is specified, transaction will not begin. If TxEnumerator is not assigned to a S2 attribute, it will default to NotSupported(Enumerator). Furthermore, to use transaction, a dicon file with TransactionContext and DataSource definition is necessary. 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=server name;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()
        {
            // rollback transaction when method ends
        }
    }
VB.NET
    <TestFixture> _
    Public Class HogeTest
         Inherits S2TestCase

        Public  Sub SetUpHogeHoge()
            Include("Ado.dicon")
        End Sub

        <Test, S2(Tx.Rollback)> _
        Public  Sub TestHogeHoge()
            ' rollback transaction when method ends
        End Sub

    End Class
## Testing with a database S2, also, makes it easier to conduct tests using a database. Below is an example using S2ADO framework. ### Test using Select command In this example, employee table is queried using employee number as a key. When employee table is queried for employee number 9900, that row joined with corresponding row from the deptartment table is returned. The test case for this example should consist of expected employee table row and department table row. This expected data is specified in an Excel sheet - sheet name should correspond to the database table name, first row in the sheet should contain column names, and expected row data should be specified from the second row. The data may be manually entered into the Excel sheets or generated from the database table. To run this example, first Setup a database to create a demostration database table used in this example. Excel sheets may then be filled with data from the database table using tool Db2ExcelClient in Seasar.Examples. Seasar/Examples/Reference/S2Unit/Db2ExcelClient.dicon
    <components>
      <include path="Seasar.Examples/Ado.dicon" />
      <component name="Db2ExcelClient" class="Seasar.Examples.Reference.S2Unit.Db2ExcelClient"/>
      <component class="Seasar.Extension.DataSets.Impl.SqlReader">
        <initMethod>self.AddTable("emp", "empno = 7788")</initMethod>
        <initMethod>self.AddTable("dept", "deptno = 20")</initMethod>
      </component>
      <component class="Seasar.Extension.DataSets.Impl.XlsWriter" instance="prototype">
        <arg>"Seasar.Examples/Reference/S2Unit/GetEmployeePrepare.xls"</arg>
      </component>
    </components>
SqlReader fills DataSet from the database tables. The first argument to AddTable is the tableName(sheetName). The second argment should contain the condition. XlsWriter outputs DataSet to Excel. File path relative to the output folder is specified in the constructor. Seasar/Examples/Reference/S2Unit/Db2ExcelClient
    using System;
    using Seasar.Extension.DataSets.Impl;
    using Seasar.Framework.Container;
    using Seasar.Framework.Container.Factory;

    namespace Seasar.Examples.Reference.S2Unit
    {
        public class Db2ExcelClient
        {
            private const string PATH = "Seasar.Examples/Reference/S2Unit/Db2ExcelClient.dicon";

            public Db2ExcelClient() { }

            public void Main()
            {
                IS2Container container = S2ContainerFactory.Create(PATH);
                container.Init();
                try
                {
                    SqlReader reader = (SqlReader) container.GetComponent(typeof(SqlReader));
                    XlsWriter writer = (XlsWriter) container.GetComponent(typeof(XlsWriter));
                    writer.Write(reader.Read());
                    Console.Out.WriteLine("output Excel File : {0}", writer.FullPath);
                }
                catch (ApplicationException e)
                {
                    Console.Out.WriteLine(e.Message);
                }
            }
        }
    }
Data from a database table may be written out to an Excel sheet by executing Read() method in SQLReader class retrieved from IS2Container and then executing Write() method in XlsWrite class retrieved from IS2Container. Check to make sure the data was created by executing Seasar.Examples, selecting S2Container reference from the tree, selecting "Test on Select Query(1)". There should be GetEmployeePrepare.xls in the folder Visual Studio output path + specified path (example: bin\Debug\Seasar.Examples\Reference\S2Unit). Double click on GetEmployeePrepare.xls to open this Excel sheet. In emp sheet, change EMPNO to 9900, ENAME to SCOTT2, DEPTNO to 99. In the dept sheet, change DEPTNO to 99, DNAME to RESEARCH2. Save and close the sheets. Next, it is necessary to create data to verify data. Db2ExcelClient2 (available in Seasar.Examples) may be used to create this data. Seasar/Examples/Reference/S2Unit/Db2ExcelClient2.dicon
    <components>
      <include path="Seasar.Examples/Ado.dicon" />
      <component name="EmployeeDaoTest" class="Seasar.Examples.Reference.S2Unit.EmployeeDaoTest"/>
      <component class="Seasar.Examples.Reference.S2Unit.EmployeeDao">
        <property name="GetEmployeeHandler">
          <component class="Seasar.Extension.ADO.Impl.BasicSelectHandler">
            <property name="Sql">
    "SELECT e.empno, e.ename, e.deptno, d.dname
    FROM emp e, dept d WHERE e.empno = @empno AND e.deptno = d.deptno"</property>
          </component>
        </property>
      </component>
    </components>
Execute Seasar.Examples, select S2Container reference from the tree, select "Test on Select Query(2)". Create GetEmployeeResult.xls in folder Visual Studio output path + specified path (example: bin\Debug\Seasar.Examples\Reference\S2Unit). In the emp sheet, change EMPNO to 9900, ENAME to SCOTT2, DEPTNO to 99, DNAME to RESEARCH2. Save and close the sheets after making the changes. Seasar/Examples/Reference/S2Unit/EmployeeDao.dicon
    <components>
      <include path="Seasar.Examples/Ado.dicon" />
      <component name="EmployeeDaoTest" class="Seasar.Examples.Reference.S2Unit.EmployeeDaoTest"/>
      <component class="Seasar.Examples.Reference.S2Unit.EmployeeDao">
        <property name="GetEmployeeHandler">
          <component class="Seasar.Extension.ADO.Impl.BasicSelectHandler">
            <property name="Sql">
    "SELECT e.empno, e.ename, e.deptno, d.dname
    FROM emp e, dept d WHERE e.empno = @empno AND e.deptno = d.deptno"</property>
          </component>
        </property>
      </component>
    </components>
Seasar/Examples/Reference/S2Unit/EmployeeDaoTest.cs
    using System.Data;
    using MbUnit.Core.Cons;
    using MbUnit.Framework;
    using Seasar.Extension.DataSets.Impl;
    using Seasar.Extension.Unit;

    namespace Seasar.Examples.Reference.S2Unit
    {
        [TestFixture]
        public class EmployeeDaoTest : S2TestCase
        {
            private IEmployeeDao dao_ = null;

            public void SetUpGetEmployee()
            {
                Include("Seasar.Examples/Reference/S2Unit/EmployeeDao.dicon");
            }

            [Test, S2(Tx.Rollback)]
            public void GetEmployee()
            {
                ReadXlsWriteDb("Seasar.Examples/Reference/S2Unit/GetEmployeePrepare.xls");
                Employee emp = dao_.GetEmployee(9900);
                DataSet expected = ReadXls("Seasar.Examples/Reference/S2Unit/GetEmployeeExpected.xls");
                S2Assert.AreEqual(expected, emp, "1");
            }

            public void Main()
            {
                using (MainClass mc = new MainClass())
                {
                    mc.Main(new string[] { "Seasar.Examples.exe" });
                }
            }
        }
    }
Excecute Seasar.Examples. To start testing, select S2Container Reference from the tree. Select Test on Select query(3). Test may be executed from the console by executing MbUnit, but it is easier to use MbUnit.GUI.exe and TestDriven.NET. SetUpGetEmployee() as app.dicon. S2Assert.AreEqual() method compares the DataSet with IList of IDictionary and IDictionary, object, and IList of object. ReadXlsWriteDb() method and ReadXlsAllReplaceDb() methods are used to write test data to the database. If Excel file is in the same namespace as the test class, namespace may be omitted from the path. (However, it is necessary to use build event to output to the same folder as the assembly.) To rollback after each test, execute ReadXlsWriteDb() andReadXlsAllReplaceDb() at the beginning of the test method. Execution result are compared with data in an Excel sheet. By passing data retrieved from Dao and verification data in Excel sheet retrieved from ReadXls() method to the S2Assert.AreEqual() method, data retrieved from Dao is converted to DataSet and is used in comparison with data from the Excel sheet. ## Restrictions S2Unit.NET reads sheet in alphabetical order and not in the order they are defined in the Excel sheets as in S2Unit.Java. If there is an error due to foreign key constraint, prefix sheet name with "#". For example, to insert data from the emp table into the third sheet, name the Excel sheet "#3 emp". |
Trans. by H.Ozawa | Copyright© 2004-2007, The Seasar Foundation and the others. All rights reserved.