手写 TestNG

本贴最后更新于 1126 天前,其中的信息可能已经时移世异

1、TestNG简介

TestNG是一个强大的测试框架,深受JUnit和NUnit启发的测试框架,并且引入了一些新功能,使其功能更强大,更易于使用。它突破了以前一些框架的限制,为开发人员和测试人员提供了方便强大的编写和测试。它支持所有的测试活动包括单元测试、集成测试和UI系统测试,NG指Next Generation,被视为是Junit的升级版。

如此强大的TestNG常用来搭建接口自动化框架和UI自动化框架,为了更好的使用和理解TestNG,接下来就通过上一篇中提到的反射造一个轮子。

2、实现TestNG Hello World案例

用过的TestNG的同学,对于Hello World 案例一定不陌生,没用的也不用担心,所谓TestNG Hello World案例非常简单,TestNG核心是通过testng.xml配置测试类信息,然后执行测试类中带有@Test的方法。

举个例子:

testng.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<suite name="suite1">
    <test name="test1">
        <classes>
            <class name="com.lemon.testng.TestNGDemo">
            </class>
        </classes>
    </test>
</suite>

TestNGDemo 内容如下:

public class TestNGDemo {

    @Test
    public void test1() {
        System.out.println("TestNGDemo.test1");
    }
  
    @Test
    public void test2() {
        System.out.println("TestNGDemo.test2");
    }
  
    @Test
    public void test3() {
        System.out.println("TestNGDemo.test3");
    }
   

}

当我们运行testng.xml时,就会去执行test1、test2、test3方法,这就是TestNG Hello World案例。

实现这个功能需要用到xml解析和反射两个技术,不算太难。

接下来就动起手来吧。

1、解析xml,使用dom4j技术。

List<String> classNames = new ArrayList<String>();
//1、创建SAXReader对象。
SAXReader reader = new SAXReader();
//2、指定xml的位置
File file = new File("testng2.xml");
//2.1、读取xml获得document对象。
Document doc = reader.read(file);
//3、获取根元素
Element rootElement = doc.getRootElement();
//4、获取根元素下面的子元素及其属性。
List<Element> elements = rootElement.elements();
//4.1、遍历所有test子元素
for (Element element : elements) {
	//4.2、遍历所有classes子元素
	List<Element>  classesElements = element.elements();
	for (Element classesElement : classesElements) {
		//4.3、遍历所有class子元素
		List<Element> classElements = classesElement.elements();
		for (Element classElement : classElements) {
			//4.4、获取name属性
			String className = classElement.attribute("name").getData().toString();
			//4.5、把所有的name存储到集合中
			classNames.add(className);
		}
	}
}

2、反射

for (String className : classNames) {
    Map<String,Method> methods = new TreeMap<>();
    //1、获取测试类(TestNGDemo)字节码对象
    Class<?> clazz = Class.forName(className);
    //2、创建测试类对象
    Object o = clazz.newInstance();
    //3、获取测试类所有方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
        //3.1、判断方法是否包含@Test注解
        if(declaredMethod.isAnnotationPresent(Test.class)) {
            //3.2、包含@Test注解的方法加入集合中。
            methods.put(declaredMethod.getName(),declaredMethod);
        }
    }
    //4、执行测试方法
    methods.forEach((k,v)->{
        //4.1、打印测试方法相关信息
        System.out.println(v.getDeclaringClass().getName() + ":" + v.getName());
        try {
            //4.2、反射调用@Test测试方法
            v.invoke(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

3、输出结果

com.lemon.testng.TestNGDemo:test1
TestNGDemo.test1
com.lemon.testng.TestNGDemo:test2
TestNGDemo.test2
com.lemon.testng.TestNGDemo:test3
TestNGDemo.test3

4、总结

上面的例子虽然简单,没有实现诸如方法依赖、优先级、前置条件等功能,却窥一斑而知全豹。TestNG大部分功能都是基于反射实现,这样设计的框架才具备通用性,像常用的一些工具类、中间件中也大量使用反射就是这个原因。

好了,轮子造完了,是不是对反射更有感觉了呢?欢迎留言讨论。

1 操作
luojie 在 2021-03-29 16:33:52 更新了该帖
2 回帖
请输入回帖内容 ...
  • j731971603

    虽然看起来很好,,但是实际上没啥用。。

  • 13047607738

    确实没什么用