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测试找不到数据库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337469.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python float 精度_浅谈Python里面小数点精度的控制

要求较小的精度 round()内置方法 这个是使用最多的&#xff0c;刚看了round()的使用解释&#xff0c;也不是很容易懂。round()不是简单的四舍五入的处理方式。 For the built-in types supporting round(), values are rounded to the closest multiple of 10 to the power min…

【C语言】指针进阶第二站:指针数组!

点击蓝字关注我们指针数组数组是一种类型的数的集合整型数组的元素都是int类型指针数组的元素都是指针变量int* arr1[10];//整型指针的数组char*arr2[10];//一级字符指针的数组char** arr3[5];//二级字符指针的数组参考这一份示意图示例1:定义多个字符指针在上一站的字符指针里…

Python3不用str自带lower转换位小写字母

Python3不用str自带lower转换位小写字母原题 https://leetcode-cn.com/problems/to-lower-case/ 题目&#xff1a; 不准用字符串自带的lower() API&#xff01; 实现函数 ToLowerCase()&#xff0c;该函数接收一个字符串参数 str&#xff0c;并将该字符串中的大写字母转换成小…

php 命令执行crud_使用活动记录执行CRUD

php 命令执行crud本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言…

python所有的父类_object代表所有类的父类吗??

作者&#xff1a;邹冲 链接&#xff1a;https://www.zhihu.com/question/19754936/answer/202650790 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 写东西的时候刚好遇到这个问题&#xff0c;回答一波…… 继承 obje…

C语言初学者常见错误 | 总结22点

点击蓝字关注我们正文一.语言使用错误在打代码的过程中&#xff0c;经常需要在中文与英文中进行转换&#xff0c;因此常出现一些符号一不小心就用错&#xff0c;用成中文。例如&#xff1a;“&#xff1b;”中文中的分号占用了两个字节&#xff0c;而英文中“;”分号只占用一个…

Python3实现队列

Python3实现队列class MyQueue:def __init__(self):self.__arr []def push(self, x: int) -> None:self.__arr.append(x)def pop(self) -> int:return self.__arr.pop(0)def peek(self) -> int:return self.__arr[0]def empty(self) -> bool:return len(self.__ar…

nginx配置vue项目500_一个Nginx部署多个vue前端项目总结

摘要&#xff1a;近来接手了一个二次开发的前后端分离模式的项目&#xff0c;其中在前端项目的部署上需要让2个前端项目都部署到一个IP地址和端口下&#xff0c;那么我们这里就要用到Nginx了&#xff0c;接下来我们看看如何在一个Nginx下部署2个前端项目的编译打包2个前端项目执…

vaadin_Vaadin和DukeScript中的Hello World

vaadin从表面上看 &#xff0c; Vaadin和GWT通常与DukeScript有很多共同点。 两者都致力于为Java开发人员提供面向浏览器的解决方案&#xff0c;并且由于其对Maven的原生支持&#xff0c;因此与IDE集成良好。 但是&#xff0c;这些方面实际上是它们共有的全部。 从编程模型到框…

【C语言】指针进阶第三站,数组指针!

点击蓝字关注我们数组指针整型指针&#xff1a;指向整型的指针字符指针&#xff1a;指向字符的指针数组指针&#xff1a;指向数组的指针基本概念下面哪个是数组指针呢&#xff1f;指针数组和数组指针的概念很容易混淆&#xff0c;一定要分清楚哦&#xff01;int *p1[10]; int (…

Python3判断是否为回文数

Python3判断是否为回文数原题 https://leetcode-cn.com/problems/palindrome-number/ 题目&#xff1a; 不允许用str的方法&#xff01; 判断一个整数是否是回文数。回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。…

lambda 序列化_如何以及为什么要序列化Lambda

lambda 序列化总览 lambda序列化在许多用例中很有用&#xff0c;例如持久配置或作为远程资源的访客模式 。 远程访客 例如&#xff0c;因此我想访问远程Map上的资源&#xff0c;可以使用get / put&#xff0c;但是说我只想从Map的值中返回一个字段&#xff0c;我可以将lambda作…

python进行数据查询_如何进行python数据库查询?(实例解析)

在这篇文章之中我们来了解一下关于python数据库的相关知识&#xff0c;有些朋友可能是刚刚接触到python这一编程语言&#xff0c;对这一方面不是特别的了解&#xff0c;在接下来这篇文章将会来带大家来了解关于python查询数据库中数据的相关知识。 数据库的查询操作 Python查询…

【C语言】指针进阶第四站:数组/指针的传参问题!

点击蓝字关注我们朋友们&#xff0c;到站啦&#xff01;指针进阶第四站&#xff1a;传参问题0.引例自定义函数里形参的类型&#xff0c;要和函数调用中传过去的实参类型相对应test函数里的是int类型&#xff0c;我们传过去的参数a也是int类型void test(int n) {} int main() {i…

python做自动化控制postman_python自动化测试入门篇-postman

接口测试基础-postman 常用的接口有两种&#xff1a;webservice接口和http api接口。 Webservice接口是走soap协议通过http传输&#xff0c;请求报文和返回报文都是xml格式。 http api接口是走http协议&#xff0c;通过路径来区分调用的方法&#xff0c;请求报文都是key-value形…

Python3实现红黑树[上篇]

Python3实现红黑树[上篇]由于时间有限&#xff0c;这次只写了红黑树添加节点&#xff0c;关于节点的删除放在下一讲 https://blog.csdn.net/qq_18138105/article/details/105324025。 关于红黑树的介绍&#xff0c;来由&#xff0c;性质和定义&#xff0c;可以看这篇文章&…

web应用程序并发测试_测试并发应用

web应用程序并发测试本文是我们名为Java Concurrency Essentials的学院课程的一部分。 在本课程中&#xff0c;您将深入探讨并发的魔力。 将向您介绍并发和并发代码的基础知识&#xff0c;并学习诸如原子性&#xff0c;同步和线程安全性的概念。 在这里查看 &#xff01; 目录…

深入理解存储器层次结构

点击蓝字关注我们1概述对于一个简单的计算机系统模型&#xff0c;我们可以将存储器系统看做是一个线性的字节数组&#xff0c;而 CPU 能够在一个常数时间内访问每个存储器的位置。实际上&#xff0c;存储器系统&#xff08;memory system&#xff09;是一个具有不同容量、成本和…

python中可选参数是什么意思_Python如何定义有可选参数的元类

问题 你想定义一个元类&#xff0c;允许类定义时提供可选参数&#xff0c;这样可以控制或配置类型的创建过程。 解决方案 在定义类的时候&#xff0c;Python允许我们使用 metaclass关键字参数来指定特定的元类。 例如使用抽象基类&#xff1a; from abc import ABCMeta, abstra…

C++的一个指针占内存几个字节?

C的一个指针占内存几个字节&#xff1f;结论&#xff1a; 取决于是64位编译模式还是32位编译模式&#xff08;注意&#xff0c;和机器位数没有直接关系&#xff09; 在64位编译模式下&#xff0c;指针的占用内存大小是8字节在32位编译模式下&#xff0c;指针占用内存大小是4字…