junit测试找不到数据库_将数据库日志添加到JUnit3
junit測試找不到數據庫
在過去的十年中,我們已經編寫了成千上萬的JUnit3測試,現在正嘗試將結果合并到數據庫中,而不是分散的日志文件中。 事實證明,擴展TestCase類非常容易做到這一點。 注意:這種方法并不直接適用于JUnit4或其他測試框架,但是通??梢宰鲱愃频氖虑椤?
被測類及其測驗
出于演示目的,我們可以使用單個方法來定義一個類進行測試。
public class MyTestedClass {public String op(String a, String b) {return ((a == null) ? "" : a) + ":" + ((b == null) ? "" : b);} }具有單個要測試方法的類比您想象的要少的限制。 在前面提到的數千個測試中,我們僅測試了四種方法。
這是上述類的一些測試。
public class MySimpleTest extends SimpleTestCase {private MyTestedClass obj = new MyTestedClass();public void test1() {assertEquals("a:b", obj.op("a", "b"));}public void test2() {assertEquals(":b", obj.op(null, "b"));}public void test3() {assertEquals("a:", obj.op("a", null));}public void test4() {assertEquals(":", obj.op(null, null));}public void test5() {// this will failassertEquals(" : ", obj.op(null, null));} }使用TestListener捕獲基本信息
JUnit3允許向偵聽器添加其測試過程。 在測試運行之前和之后以及測試失敗或有錯誤(引發異常)的任何時間調用此偵聽器。 該TestListener將基本測試信息寫入System.out,以作為概念證明。 修改它以將信息寫入數據庫,JMS主題等很容易。
public class SimpleTestListener implements TestListener {private static final TimeZone UTC = TimeZone.getTimeZone("UTC");private long start;private boolean successful = true;private String name;private String failure = null;SimpleTestListener() {}public void setName(String name) {this.name = name;}public void startTest(Test test) {start = System.currentTimeMillis();}public void addError(Test test, Throwable t) {// cache information about error.successful = false;}public void addFailure(Test test, AssertionFailedError e) {// cache information about failure.failure = e.getMessage();successful = false;}/*** After the test finishes we can update the database with statistics about* the test - name, elapsed time, whether it was successful, etc.*/public void endTest(Test test) {long elapsed = System.currentTimeMillis() - start;SimpleDateFormat fmt = new SimpleDateFormat();fmt.setTimeZone(UTC);System.out.printf("[%s, %s, %s, %d, %s, %s]\n", test.getClass().getName(), name, fmt.format(new Date(start)),elapsed, failure, Boolean.toString(successful));// write any information about errors or failures to database.} }生產TestListener應該在錯誤和失敗方面做更多的事情。 我將其忽略,以便專注于更廣泛的問題。
該偵聽器不是線程安全的,因此我們將要使用Factory模式為每個測試創建一個新實例。 我們可以在工廠中創建重量級對象,例如,在工廠中打開SQL DataSource并將新的Connection傳遞給每個實例。
public class SimpleTestListenerFactory {public static final SimpleTestListenerFactory INSTANCE = new SimpleTestListenerFactory();public SimpleTestListenerFactory() {// establish connection data source here?}public SimpleTestListener newInstance() {// initialize listener.SimpleTestListener listener = new SimpleTestListener();return listener;} }如果我們知道測試框架是純串行的,則可以通過創建緩沖區并在startTest()中調用System.setOut(),然后在endTest()中還原原始System.out來捕獲所有控制臺輸出。 只要測試永不重疊,此方法就行得通,否則會引起問題。 但是,這可能會出現問題– IDE可能具有自己的允許并行執行的測試運行程序。
我們用自己的方法覆蓋標準的run()方法,該方法在調用現有的run()方法之前創建并注冊一個偵聽器。
public class SimpleTestCase extends TestCase {public void run(TestResult result) {SimpleTestListener l = SimpleTestListenerFactory.INSTANCE.newInstance();result.addListener(l);l.setName(getName());super.run(result);result.removeListener(l);}}現在,我們將預期的結果發送到System.out。
[MySimpleTest, test1, 8/2/15 11:58 PM, 0, null, true] [MySimpleTest, test2, 8/2/15 11:58 PM, 10, null, true] [MySimpleTest, test3, 8/2/15 11:58 PM, 0, null, true] [MySimpleTest, test4, 8/2/15 11:58 PM, 0, null, true] [MySimpleTest, test5, 8/2/15 11:58 PM, 4, expected same:<:> was not:< : >, false]使用外觀和TestListener捕獲呼叫信息
這是一個好的開始,但我們可能會做得更好。 在上面提到的數千個測試中,僅調用了4種方法-如果我們可以捕獲這些調用的輸入和輸出值,則將非常強大。
如果由于某些原因不接受AOP,則可以使用AOP或日志記錄外觀包裝這些功能。 在簡單的情況下,我們可以簡單地捕獲輸入和輸出值。
public class MyFacadeClass extends MyTestedClass {private MyTestedClass parent;private String a;private String b;private String result;public MyFacadeClass(MyTestedClass parent) {this.parent = parent;}public String getA() {return a;}public String getB() {return b;}public String getResult() {return result;}/*** Wrap tested method so we can capture input and output.*/public String op(String a, String b) {this.a = a;this.b = b;String result = parent.op(a, b);this.result = result;return result;}}我們像以前一樣記錄基本信息,并添加一些新代碼來記錄輸入和輸出。
public class AdvancedTestListener extends SimpleTestListener {AdvancedTestListener() {}/*** Log information as before but also log call details.*/public void endTest(Test test) {super.endTest(test);// add captured inputs and outputsif (test instanceof MyAdvancedTest) {MyTestedClass obj = ((MyAdvancedTest) test).obj;if (obj instanceof MyFacadeClass) {MyFacadeClass facade = (MyFacadeClass) obj;System.out.printf("[, , %s, %s, %s]\n", facade.getA(), facade.getB(), facade.getResult());}}} }日志現在顯示基本信息和呼叫詳細信息。
[MyAdvancedTest, test2, 8/3/15 12:13 AM, 33, null, true] [, , null, b, :b] [MyAdvancedTest, test3, 8/3/15 12:13 AM, 0, null, true] [, , a, null, a:] [MyAdvancedTest, test4, 8/3/15 12:13 AM, 0, null, true] [, , null, null, :] [MyAdvancedTest, test1, 8/3/15 12:13 AM, 0, null, true] [, , a, b, a:b]我們希望將基本詳細信息和呼叫詳細信息相關聯,但是通過添加唯一的測試ID可以輕松實現。
在現實世界中,這種方法還不夠,在單個測試中,被測方法可能被多次調用。 在這種情況下,我們需要一種緩存多組輸入和輸出值的方法,或者擴展偵聽器,以便我們可以在每個涵蓋方法的末尾調用它。
通過將結果編碼為XML或JSON而不是簡單的列表,可以使結果更具擴展性。 這將使我們僅捕獲感興趣的值或輕松處理將來添加的字段。
[MyAdvancedTest, test2, 8/3/15 12:13 AM, 33, null, true] {"a":null, "b":"b", "results":":b" } [MyAdvancedTest, test3, 8/3/15 12:13 AM, 0, null, true] {"a":"a", "b":null, "results":"a:" } [MyAdvancedTest, test4, 8/3/15 12:13 AM, 0, null, true] {"a":null, "b":null, "results":":" } [MyAdvancedTest, test1, 8/3/15 12:13 AM, 0, null, true] {"a":" a", "b":"b", "results":" a:b" }捕獲
現在,我們可以通過重放捕獲的輸入來重新運行測試,但是盲目比較結果存在兩個問題。 首先,如果我們只關心單個值,這將是很多不必要的工作。 其次,許多測試是不確定的(例如,它們使用隨時間變化的固定數據甚至實時數據),而我們不關心的事情可能會改變。
這不是一個容易的問題。 如果幸運的話,測試將遵循標準模式,我們可以對正在執行的測試做出很好的猜測,但需要手動進行驗證。
首先,我們需要使用捕獲某些或所有方法調用的外觀包裝測試方法的結果。 調用歷史記錄應該以一種我們以后可以重播的形式提供,例如一系列方法名稱和序列化參數。
其次,我們需要包裝TestCase assertX方法,以便捕獲最近的方法調用以及傳遞給assert調用的值(當然還有結果)。
例
通過示例最容易展示和刪除該過程。 讓我們從一個簡單的POJO開始。
public class Person {private String firstName;private String lastName;public String getFirstName() { return firstName; }public String getLastName() { return lastName; } }在這種情況下,我們的外觀僅需要記錄方法名稱。
典型的測試方法是
public void test1() {Person p = getTestPerson();assertEquals("John", p.getFirstName());assertEquals("Smith", p.getLastName()); }使用包裝的assertX方法
static PersonFacade person;public static void assertEquals(String expected, String actual) {// ignoring null handling...boolean results = expected.equals(actual);LOG.log("assertEquals('" + expected + "',"+person.getMethodsCalled()+ ") = " + results);person.clearMethodsCalled();if (!results) {throw new AssertionFailedError("Expected same:<" + expected + " > was not:<" + actual + ">");} }所以我們會得到像
assertEquals('John', getFirstName()) = true; assertEquals('Smith', getLastName()) = false;不難看出如何通過測試框架來解析它,但是現在還為時過早。 第二種測試方法是
public void test1() {Person p = getTestPerson();assertEquals("john", p.getFirstName().toLowerCase()); }并且我們的簡單代碼不會捕獲toLowerCase() 。 我們的日志將錯誤記錄:
assertEquals('John', getFirstName()) = false;更為病理的情況是:
public void test1() {Person p = getTestPerson();LOG.log("testing " + p.getFirstName());assertEquals("john", "joe"); }斷言與包裝的類無關。
有明顯的創可貼,例如,我們可以捕獲外觀中的返回值,但這是一個非常深的兔子洞,我們希望遠離它。 我認為答案是做出合理的第一次嘗試,手動驗證結果,然后再做。 (替代:將測試重寫為可以捕獲的形式。)
翻譯自: https://www.javacodegeeks.com/2015/08/adding-database-logging-to-junit3.html
junit測試找不到數據庫
總結
以上是生活随笔為你收集整理的junit测试找不到数据库_将数据库日志添加到JUnit3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 漫反射 高光反射_如何有效地使用反射
- 下一篇: 劳务登记备案(劳务站备案)