• junit测试延伸--方法的重复测试


    在实际编码测试中,我们有的时候需要对一个方法进行多次测试,那么怎么办呢?这个问题和测试套件解决的方案一样,我们总不能不停的去右键run as,那怎么办呢?还好伟大的junit帮我们想到了。

    OK,现在我们开始来写一个例子:

    测试源码:

    package org.linkinpark.junit.testjunit;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月5日
     * @功能描述: 写一个测试源码
     */
    public class Linkin
    {
    	public String test(String str)
    	{
    		return str + "。。。";
    	}
    
    }
    
    测试代码:

    package org.linkinpark.junit.testjunit;
    
    import org.junit.Assert;
    
    import junit.extensions.RepeatedTest;
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    
    public class LinkinTest extends TestCase
    {
    	public LinkinTest()
    	{
    		super();
    	}
    
    	public LinkinTest(String name)
    	{
    		super(name);
    	}
    
    	public static Test suite()
    	{
    		TestSuite suite = new TestSuite();
    		suite.addTest(new RepeatedTest(new LinkinTest("test"), 20));
    		return suite;
    	}
    
    	public void test() throws Exception
    	{
    		Linkin test = new Linkin();
    		String str = test.test("LinkinPark");
    		Assert.assertEquals("LinkinPark。。。", str);
    	}
    
    }
    
    现在我们运行一下,然后我们看下junit控制台什么情况?

    OK,测试通过,我们也看到了,我们写的这个test测试方法执行20次,现在我们既然会使用junit来进行方法的重复测试了。那么junit到底是怎么实现的呢?我在前面的junit源码解析整理中没有整理到这部分。

    那我们现在一起来看一下:

    上面的测试代码使用junit38测试套件,就是自定义了Suite()方法,我不知道junit4X系列中这个重复测试能不能用,个人觉得好像是不行。先言归正传吧。在我们自己定义的Suite类中,我们调用addTest()方法来添加用例,注意这里添加的是RepeatedTest类,那我们现在就来看看这个类的源码:

    RepeatedTest类的父类TestDecorator源码如下:

    package org.linkinpark.commons.extensions;
    
    import org.linkinpark.commons.framework.Test;
    import org.linkinpark.commons.framework.TestResult;
    import org.linkinpark.junit.Assert;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月5日
     * @功能描述: 测试装饰器父类
     */
    public class TestDecorator extends Assert implements Test
    {
    	protected Test fTest; // 封装一个测试用例
    
    	public TestDecorator(Test test)
    	{
    		fTest = test;
    	}
    
    	/**
    	 * The basic run behaviour.
    	 */
    	public void basicRun(TestResult result)
    	{
    		fTest.run(result);
    	}
    
    	public int countTestCases()
    	{
    		return fTest.countTestCases();
    	}
    
    	public void run(TestResult result)
    	{
    		basicRun(result);
    	}
    
    	@Override
    	public String toString()
    	{
    		return fTest.toString();
    	}
    
    	public Test getTest()
    	{
    		return fTest;
    	}
    }
    RepeatedTest源码如下:

    package org.linkinpark.commons.extensions;
    
    import org.linkinpark.commons.framework.Test;
    import org.linkinpark.commons.framework.TestResult;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月5日
     * @功能描述: 重复测试Test容器
     */
    public class RepeatedTest extends TestDecorator
    {
    	private int fTimesRepeat;
    
    	public RepeatedTest(Test test, int repeat)
    	{
    		super(test);
    		if (repeat < 0)
    		{
    			throw new IllegalArgumentException("Repetition count must be >= 0");
    		}
    		fTimesRepeat = repeat;
    	}
    
    	@Override
    	public int countTestCases()
    	{
    		return super.countTestCases() * fTimesRepeat;
    	}
    
    	@Override
    	public void run(TestResult result)
    	{
    		for (int i = 0; i < fTimesRepeat; i++)
    		{
    			if (result.shouldStop())
    			{
    				break;
    			}
    			super.run(result);
    		}
    	}
    
    	@Override
    	public String toString()
    	{
    		return super.toString() + "(repeated)";
    	}
    }

    2篇源码分析一下:

    TestDecorator父类中,封装一个测试用例Test。在前面的一系列junit源码分析中,我们知道了,一个测试用例的执行,实际是TestCase类中的run()方法。其实这里的这个TestDecorator可以理解为类似于Suite,他是一个测试用例的容器。

    由于该类同样实现了Test接口,所以执行测试也同样是run()方法,那么我们看到了该类的run()方法实际上调用的也是Test接口的run()方法,这么说大家明白了,其实这里只是在TestCase类上面嫁入一层,其实也是适配器模式。

    然后现在我们来看下RepeatedTest类,该子类重写了父类的run()方法,只不过加了一个for循环来重复调用父类执行测试的方法,这样子就实现了多次执行测试的目的。源码不难,我们在研究junit的源码过程中不得不佩服junit的设计。这块也是我之所以

    要去看一些开源框架源码的目的,之后我会自己写一套自己的web框架,到时候也会把这些好的思想加入进去,使自己的代码真正的高内聚,低耦合。


    OK,在看前面的源码过程中,我还发现了一个类,TestSetup,同样也是TestDecorator的子类。大概的看了下源码,也明白了该类的使用。

    package org.linkinpark.commons.extensions;
    
    import org.linkinpark.commons.framework.Protectable;
    import org.linkinpark.commons.framework.Test;
    import org.linkinpark.commons.framework.TestResult;
    
    /**
     * A Decorator to set up and tear down additional fixture state. Subclass
     * TestSetup and insert it into your tests when you want to set up additional
     * state once before the tests are run.
     */
    public class TestSetup extends TestDecorator
    {
    
    	public TestSetup(Test test)
    	{
    		super(test);
    	}
    
    	@Override
    	public void run(final TestResult result)
    	{
    		Protectable p = new Protectable()
    		{
    			public void protect() throws Exception
    			{
    				setUp();
    				basicRun(result);
    				tearDown();
    			}
    		};
    		result.runProtected(this, p);
    	}
    
    	/**
    	 * Sets up the fixture. Override to set up additional fixture state.
    	 */
    	protected void setUp() throws Exception
    	{
    	}
    
    	/**
    	 * Tears down the fixture. Override to tear down the additional fixture
    	 * state.
    	 */
    	protected void tearDown() throws Exception
    	{
    	}
    }


    明显的如果我们在测试类中存在继承关系的时候,我们当然可以直接重写setUp()和tearDown()方法来执行测试前后的初始化,同时我们也可以使用装饰器来封装一次测试用例,同样的效果。我看过junit源码中自带的测试类,框架主要用这个类来做一些异常和失败的统计。OK,我们学段代码来试一下好了。

    演示代码如下:

    package org.linkinpark.commons.textui;
    
    import org.linkinpark.commons.framework.TestCase;
    import org.linkinpark.junit.Assert;
    
    public class LinkinTest extends TestCase
    {
    	public LinkinTest()
    	{
    	}
    
    	public LinkinTest(String methodName)
    	{
    		super(methodName);
    	}
    
    	public void setUp()
    	{
    		System.err.println("这里是父类定义的setUp()");
    	}
    	
    
    	public void testLinkin4Normal()
    	{
    		final String str = "林肯:这里是自己的被测试的正确代码";
    		Assert.assertEquals(str, str);
    	}
    
    
    }
    
    然后我们派生一个子类,主要在子类执行main方法时候不要直接运行,写一个suite静态方法来传入一个TestSetup类型的Test,然后我们来看下效果。

    package org.linkinpark.commons.textui;
    
    import org.linkinpark.commons.extensions.TestSetup;
    import org.linkinpark.commons.framework.Test;
    import org.linkinpark.commons.framework.TestSuite;
    
    public class SonLinkinTest extends LinkinTest
    {
    	
    	public static Test suite()
    	{
    		TestSuite suite = new TestSuite();
    		suite.addTest(new TestSetup(new LinkinTest()));
    		return suite;
    	}
    	
    	public void tearDown()
    	{
    		System.err.println("这里是子类定义的tearDown()");
    	}
    	
    	public static void main(String[] args)
    	{
    		TestRunner.run(SonLinkinTest.class);
    	}
    
    }
    

    最后我们来看下控制台的输出:

    ###########开始迭代运行整套测试,互相独立###########
    第一步:框架开始打印日志====
    ~~~~~~~~~~~~~~~~~~~~~~~
    第二步:框架开始运行测试====
    这里是父类定义的setUp()
    框架开始执行测试,执行的方法是-->public void org.linkinpark.commons.textui.LinkinTest.testLinkin4Normal()
    框架结束执行测试,执行的方法是-->public void org.linkinpark.commons.textui.LinkinTest.testLinkin4Normal()
    这里是子类定义的tearDown()
    第三步:框架结束运行测试====
    ~~~~~~~~~~~~~~~~~~~~~~~
    第四步:框架开始统计时间====
    耗时:0.023秒
    第五步:框架开始统计结果====
    结果:OK,木问题!
    统计:一共执行了1个测试用例
    第六步:框架结束整个测试====
    

  • 相关阅读:
    兼容css3.0中的boxshadow
    获取页面和元素可视高度
    关于javascript中apply()和call()方法的区别
    BFC(Block Formatting Context)
    minheight最小高度的实现(兼容IE6、IE7、FF)
    sicily 1825. Nickname
    sicily 2000. Toy Shopping
    sicily 2075. 2.2 Computing the volume of a cylinder
    sicily 2001. Scavenger Hunt
    sicily 1608. Digit Counting
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5232877.html
Copyright © 2020-2023  润新知