<ruby id="h6500"><table id="h6500"></table></ruby>
    1. <ruby id="h6500"><video id="h6500"></video></ruby>
          1. <progress id="h6500"><u id="h6500"><form id="h6500"></form></u></progress>

            數據庫程序的單元測試

            發(fā)表于:2007-05-05來(lái)源:作者:點(diǎn)擊數: 標簽:單元測試數據庫測試單元程序
            這些筆錄是我關(guān)于已完成的數據庫 功能測試 的一些心得。其中的例子是用 java 語(yǔ)言編寫(xiě)的,但我認為這些想法對于大多數編程環(huán)境都普遍適用。當然,我仍致力于尋找更佳的 解決方案 。 現實(shí)的問(wèn)題是這樣的:你有一個(gè) SQL 數據庫,一些存儲過(guò)程和一個(gè)介于應用程
                這些筆錄是我關(guān)于已完成的數據庫功能測試的一些心得。其中的例子是用java語(yǔ)言編寫(xiě)的,但我認為這些想法對于大多數編程環(huán)境都普遍適用。當然,我仍致力于尋找更佳的解決方案。
              現實(shí)的問(wèn)題是這樣的:你有一個(gè)SQL數據庫,一些存儲過(guò)程和一個(gè)介于應用程序和數據庫之間的中間層。你怎樣在其中插入測試代碼從而保證在數據庫中數據存取功能的實(shí)現? 一、 為什么會(huì )有這樣的問(wèn)題?
              我猜想有些,可能不完全是大多數的數據庫開(kāi)發(fā)過(guò)程都是這樣的:建立數據庫,編寫(xiě)存取數據到數據庫的代碼,編譯并運行,用一個(gè)查詢(xún)語(yǔ)句查驗所列的數據是否正確顯示。如果能正確顯示那就大功告成了。然而,這種靠眼睛來(lái)檢測的弊端在于:你不經(jīng)常進(jìn)行這樣的檢驗,而且這種檢驗是不完全的。存在這樣的可能性,當你對系統進(jìn)行了修改,過(guò)了幾個(gè)月后,你無(wú)意中破壞了系統,從而導致數據的丟失。作為一個(gè)編程人員,你可能不會(huì )花很多時(shí)間來(lái)檢查數據本身,這就使錯誤的數據要經(jīng)過(guò)較長(cháng)的時(shí)間才能暴露出來(lái)。我曾經(jīng)參與一個(gè)建立網(wǎng)站的項目,該項目中在注冊時(shí)有一個(gè)必填數據在大半年中沒(méi)有被發(fā)現未實(shí)際輸入進(jìn)數據庫。盡管公司市場(chǎng)部曾經(jīng)提出他們需要這一信息,但因為這項數據從來(lái)沒(méi)有人去看它,直接導致了這一問(wèn)題在很長(cháng)時(shí)間內沒(méi)有被發(fā)現。自動(dòng)化測試,由于它能經(jīng)常測試而且測試范圍較廣,降低了數據丟失的風(fēng)險。我發(fā)現它能使我更心安理得地休息。當然,自動(dòng)化測試還有其他一些好處,他們本身就是代碼編寫(xiě)的范例,也可以作為文檔,便于你修改別人編寫(xiě)的原始程序,從而減少檢測所需的時(shí)間。
            二、 什么是我們所談?wù)摰臏y試?
              設想有一個(gè)非常簡(jiǎn)單的用戶(hù)數據庫,包括用戶(hù)電子信箱和一個(gè)標志,用來(lái)指示郵件地址是否被彈回。你的數據庫程序應該包括插入、修改、刪除和查詢(xún)等方法
              插入方法會(huì )調用一個(gè)存儲過(guò)程將數據寫(xiě)入數據庫。為了敘述方便,這里省去了一些細節,大致的程序如下所示:
            public class UserDatabase
            {
            ...
            public void insert(User user)
            {
            PreparedStatement ps = connection.prepareCall("{ call User_insert(?,?) }");
            ps.setString(1, user.getEmail());
            ps.setString(2, user.isBad()); // In real life, this would be a boolean.
            ps.executeUpdate();
            ps.close();
            }
            ...
            }
              而我認為的測試代碼應為:
            public class TestUserDatabase extends TestCase
            {
            ...
            public void testInsert()
            {
            // Insert a test user:
            User user = new User("some@email.address");
            UserDatabase database = new UserDatabase();
            database.insert(user);
            // Make sure the data really got there:
            User db_user = database.find("some@email.address");
            assertTrue("Expected non-null result", db_user != null);
            assertEquals("Wrong email", "some@email.address", db_user.getEmail());
            assertEquals("Wrong bad flag", false, db_user.isBad());
            }
            ...
            }
              可能你還有更多測試代碼。(注意一些測試,例如對date類(lèi)的測試)。
              assertTrue和assertEquals方法進(jìn)行條件測試。如果測試失敗,他們將返回診斷消息。其重點(diǎn)是這些測試都基于一個(gè)測試框架自動(dòng)執行,并給出測試成敗的標志。這些測試都基于用java語(yǔ)言編寫(xiě)的測試框架Junit類(lèi)(程序附后)。這一框架也能適應其他諸如C, C++, Perl, Python, .NET (all languages), PL/SQL, Eiffel, Delphi, VB等語(yǔ)言環(huán)境。
              下一個(gè)問(wèn)題就是:我們有測試,但我們怎樣保證測試數據和實(shí)際數據能?chē)栏駞^分?
            三、 不同的鑒別方法
              在開(kāi)始之前,我必須指出你最好有一個(gè)測試用的數據庫,你可能更想在非正式的數據庫中實(shí)踐我講的東西。
              第一種方法是手工在數據庫中輸入一些預先知道的測試性數據,例如在郵件地址中輸入“testuser01@test.testing”。如果你正在測試數據庫的查詢(xún)功能,你能預先知道,比如說(shuō)有五個(gè),數據庫記錄是以“@test.testing”結尾的。
              由以上方式插入的數據必須由測試本身進(jìn)行必要的維護。例如,測試必須負責刪除所建立的測試數據,而避免對實(shí)際數據進(jìn)行操作,從而保證整個(gè)數據庫處于完好狀態(tài)。
              這種方法還是存在以下問(wèn)題:
              你不得不和其他編程人員進(jìn)行數據協(xié)調——假設他們也有他們自己的測試數據庫。
              在數據庫中有些特殊的數據并不正確,如一些特別的郵件地址和被保留餓編號前綴。
              在某些情況下,你將不能用一些特殊的數據來(lái)區分測試數據和實(shí)際數據,這就比較棘手。例如,某條數據由一些整數型字段構成,而作為測試用的數值都看起來(lái)較為合理。
              你的測試只限于你為測試所保留的某些特殊值,這意味著(zhù)你將小心地選擇那些特殊值。
              如果數據對時(shí)間敏感,那對數據庫的維護將更為困難。例如,數據庫中有產(chǎn)品銷(xiāo)售提議,而該提議只在明確的時(shí)間段里有效。 我曾經(jīng)試著(zhù)做過(guò)修改。例如,在數據庫中增加“is_test”字段作為區分測試數據的標志,從而避免特殊值的問(wèn)題。但由此帶來(lái)的問(wèn)題是,你的測試代碼將只測試那些標記為測試的數據,而你的正式代碼卻要處理那些未標記為測試的數據。如果你的測試在這方面有區別,你事實(shí)上并不在測試同一代碼。
            四、 你需要四個(gè)數據庫
              有些想法認為一個(gè)好的測試是足夠充分的并能建立測試所需要的全部數據。如果你能在測試進(jìn)行前就明確知道數據庫所處的狀態(tài),測試可以進(jìn)行一些簡(jiǎn)化。一個(gè)簡(jiǎn)化的方法是建立一個(gè)獨立的單元測試數據庫用于測試程序,測試程序在開(kāi)始進(jìn)行前清除測試數據庫中的全部數據。
              在代碼中,你可以編寫(xiě)一個(gè)dbSetUp方法,如下所示:
            public void dbSetUp()
            {
            // Put the database in a known state:
            // (stored procedures would probably be better here)
            helper.exec("DELETE FROM SomeSideTable");
            helper.exec("DELETE FROM User");

            // Insert some commonly-used test cases:
            ...
            }
              任何數據庫測試程序都將在做任何事前首先調用dbSetUp方法,它將使測試數據庫處于一種已知狀態(tài)(大部分情況下是空數據庫狀態(tài))。這種做法具有以下的優(yōu)點(diǎn):
              所有的測試數據都在代碼層和其他編程人員進(jìn)行交流,因此沒(méi)有必要進(jìn)行外部測試數據協(xié)調。
              無(wú)須測試用的特殊數據的介入。
              簡(jiǎn)單而容易理解的一種方法。
              在每一次測試前刪除和插入數據可能會(huì )花較多時(shí)間,但是由于測試用的數據量相對較小,我認為這種方法比較快捷,特別是在測試一個(gè)本地數據庫時(shí)。
              這種做法不利的一面是你需要至少兩個(gè)數據庫。但是請記住,他們在必要是都可以在同一個(gè)服務(wù)器上運行。采用這種方法,我用了四個(gè)數據庫,另外兩個(gè)在緊急關(guān)頭時(shí)使用,具體如下:
              1. 實(shí)際使用數據庫,包含實(shí)際數據。在這個(gè)數據庫中不進(jìn)行測試,確保數據的完整性。
              2. 你的本地開(kāi)發(fā)數據庫,用來(lái)進(jìn)行大部分的測試。
              3. 一個(gè)加入一定量數據的本地開(kāi)發(fā)數據庫,可能和其他編程人員共享,用來(lái)運行應用程序并檢測是否能在實(shí)際使用的數據庫上運行,而不是照搬實(shí)際使用數據庫中的全部數據。從嚴格意義上說(shuō)你可能并不需要這一數據庫,但這一數據庫能確保應用程序在有大量數據的數據庫中順利運行。
              4. 一個(gè)發(fā)布數據庫,或稱(chēng)集成數據庫,用來(lái)在正式發(fā)布前進(jìn)行一系列測試,從而確保對所有本地數據庫的修改都得到確認。如果你一個(gè)人開(kāi)發(fā),你可以省略這個(gè)數據庫,但你必須確保所有對數據結構和存儲過(guò)程的修改都在實(shí)際使用數據庫中得到確認。在有多個(gè)數據庫的情況下,你要確保不同數據庫間結構的同步:如果你在測試數據庫中改變表的定義或存儲過(guò)程,你必須記得在實(shí)際使用的服務(wù)器上進(jìn)行同樣的修改。發(fā)布數據庫的作用就是提醒你進(jìn)行這些修改。另外,我發(fā)現如果代碼控制系統能將提交時(shí)的注釋用郵件形式自動(dòng)發(fā)給整個(gè)開(kāi)發(fā)組,那將給團隊開(kāi)發(fā)帶來(lái)較大幫助。CVS就能做到這一點(diǎn),我希望你能利用這一功能。
            五、 在合適的數據庫中進(jìn)行測試
              在這種情況下,你必須連接正確的數據庫。在實(shí)際使用數據庫中進(jìn)行測試有可能刪除所有的有用數據,這點(diǎn)令我十分害怕。
              有幾種辦法能避免此類(lèi)悲劇的發(fā)生。例如,比較普遍的做法是將數據庫連接設置記錄在初始文件中,從而明確哪一個(gè)是測試數據庫。你也可以通過(guò)初始文件進(jìn)行本地數據庫的測試,而用其他指定方法連接實(shí)際使用數據庫。
              在java代碼中,初始文件可能如下所示;
            myapp.db.url=jdbc:mysql://127.0.0.1/mydatabase
              這一連接字符串用來(lái)連接數據庫。你可以添加第二個(gè)連接字符串來(lái)區分測試數據庫:
            myapp.db.url=jdbc:mysql://127.0.0.1/mydatabase
            myapp.db.testurl=jdbc:mysql://127.0.0.1/my_test_database
              在測試代碼中,你可以檢查并確保在連接到測試數據庫后應用程序才能繼續運行:
            public void dbSetUp()
            {
            String test_db = InitProperties.get("myapp.db.testurl");
            String db = InitProperties.get("myapp.db.url");
            if (test_db == null)
            abort("No test database configured");
            if (test_db.equals(db))
            {
            // All is well: the database we''re connecting to is the
            // same as the database identified as "for testing"
            }
            else
            {
            abort("Will not run tests against a non-test database");
            }
            }
              另一個(gè)技巧是,如果你有一個(gè)本地測試數據庫,測試程序能通過(guò)提供IP地址或主機名進(jìn)行檢測。如果不是“l(fā)ocalhost/127.0.0.1”,這就有連接在實(shí)際使用數據庫上進(jìn)行測試的風(fēng)險。
            六、 關(guān)于測試日期的體會(huì )
              如果你想存儲日期信息,你大概想確認你存的日期信息是否正確。請注意以下幾點(diǎn)。
              首先先問(wèn)自己,是誰(shuí)創(chuàng )建該日期。如果是你的應用程序,那驗證比較簡(jiǎn)單,因為你可以通過(guò)查看數據庫中的具體日期進(jìn)行比較。如果是數據庫本身創(chuàng )建該日期,可能作為一個(gè)缺省字段,那你可能就會(huì )有些問(wèn)題。例如,你能確保你代碼所代表的時(shí)區和數據庫的時(shí)區一致嗎?從沒(méi)有聽(tīng)說(shuō)過(guò)數據庫是以格林尼治標準時(shí)間為準顯示時(shí)間和日期的。你能確保運行應用程序的計算機上的時(shí)間和數據庫所在計算機上的時(shí)間保持一致嗎?如果不是,你必須在進(jìn)行時(shí)間的比較時(shí)留出一定的誤差。
              如果你遇到這些情況,有些事是你可以做的:
              如果你預先知道所用的時(shí)區,在測試前將所有日期和時(shí)間全部轉換成那個(gè)時(shí)區的日期和時(shí)間。
              在比較時(shí)間時(shí)設立一定的誤差,比如說(shuō)幾分鐘、幾小時(shí)或幾個(gè)月??瓷先ト狈φf(shuō)服力,但至少它能捕獲諸如日期為空或1970年1月1日等錯誤。
            七、 總結
              在本文中,我想說(shuō):
              單元數據庫測試是一件值得做的事;
              如果你能給一系列測試程序一個(gè)對應的數據庫,測試本身并不非??膳?。
              還有其他方法能解決這一問(wèn)題。我還不能確信模仿對象(Mock Object)這一方法。就我對這一方法的理解,模仿對象模擬了一個(gè)系統中間層(在本文中,是數據庫操作系統),使得模仿的數據庫總能返回你想要的數據。我比較欣賞這一概念,它鼓勵你對測試進(jìn)行分層,可能劃分成SQL方面的測試和Java語(yǔ)言方面的測試,從而對模仿的ResultSet對象進(jìn)行測試。
              我比較關(guān)注那些能導致一次能使兩個(gè)或兩個(gè)以上的數據表產(chǎn)生變化的操作。在這種情況下,用模仿對象方法進(jìn)行數據庫的維護和實(shí)現比較困難。當然,我還要找到一種好方法進(jìn)行數據庫中SQL方面的測試,從而確認數據被正確地存儲到數據庫中。
             

            原文轉自:http://kjueaiud.com

            ...
            老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月
              <ruby id="h6500"><table id="h6500"></table></ruby>
              1. <ruby id="h6500"><video id="h6500"></video></ruby>
                    1. <progress id="h6500"><u id="h6500"><form id="h6500"></form></u></progress>