junit单元测试的原理是什么?如何使用?

TheDisguiser 2020-07-01 17:48:45 java常见问答 5324

在当前java环境中,多数都会集成了Junit测试框架,小伙伴们知道它该怎么使用吗?它的原理又是什么呢?下面就听小编来慢慢讲解讲解吧。

Junit单元测试原理及使用

我们使用一个东西, 一定要做到知其然和知其所以然,现在,我们就通过一个简单的例子来了解一下Junit的使用及原理。

示例:

/**
 * A sample test case, testing <code>java.util.Vector</code>.
 *
 */
public class VectorTest extends TestCase
{
    protected Vector fEmpty;
    protected Vector fFull;
    public static void main(String[] args)
    {
        junit.textui.TestRunner.run(suite());
    }
    protected void setUp()
    {
        fEmpty = new Vector();
        fFull = new Vector();
        fFull.addElement(new Integer(1));
        fFull.addElement(new Integer(2));
        fFull.addElement(new Integer(3));
    }
    public static Test suite()
    {
        return new TestSuite(VectorTest.class);
    }
    public void testCapacity()
    {
        int size = fFull.size();
        for (int i = 0; i < 100; i++)
            fFull.addElement(new Integer(i));
        assertTrue(fFull.size() == 100 + size);
    }
    public void testClone()
    {
        Vector clone = (Vector) fFull.clone();
        assertTrue(clone.size() == fFull.size());
        assertTrue(clone.contains(new Integer(1)));
    }
    public void testContains()
    {
        assertTrue(fFull.contains(new Integer(1)));
        assertTrue(!fEmpty.contains(new Integer(1)));
    }
    public void testElementAt()
    {
        Integer i = (Integer) fFull.elementAt(0);
        assertTrue(i.intValue() == 1);
        try
        {
            fFull.elementAt(fFull.size());
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            return;
        }
        fail("Should raise an ArrayIndexOutOfBoundsException");
    }
    public void testRemoveAll()
    {
        fFull.removeAllElements();
        fEmpty.removeAllElements();
        assertTrue(fFull.isEmpty());
        assertTrue(fEmpty.isEmpty());
    }
    public void testRemoveElement()
    {
        fFull.removeElement(new Integer(3));
        assertTrue(!fFull.contains(new Integer(3)));
    }
}

1)、主入口

public static void main(String[] args)
{
    junit.textui.TestRunner.run(suite());
}

2)、suite()

public static Test suite()
 {
     return new TestSuite(VectorTest.class);
 }

这是JUnit使用TestSuite规定的模式,尤其是在命令行或图形界面下,只有定义了public static Test suite() 方法,框架才会按照我们的定义运行框架。

3)、new TestSuite(VectorTest.class)、

TestSuite是有三个构造函数的,一个没有参数,一个以Class为参数还有一个以Class和名字字符串作为参数。Class必须是实现了Test接口的子类,一般继承自TestCase,并且该类中定义了以Test开头没有参数的测试方法。

//构造函数
while (Test.class.isAssignableFrom(superClass))
{
    Method[] methods = superClass.getDeclaredMethods();
    for (int i = 0; i < methods.length; i++)
    {
        addTestMethod(methods[i], names, theClass);
    }
    superClass = superClass.getSuperclass();
}
//addTestMethod
if (!isPublicTestMethod(m))
{
    if (isTestMethod(m))
        addTest(warning("Test method isn't public: " + m.getName()));
    return;
}
names.addElement(name);
addTest(createTest(theClass, name));
//isTestMethod
private boolean isTestMethod(Method m)
{
    String name = m.getName();
    Class[] parameters = m.getParameterTypes();
    Class returnType = m.getReturnType();
    return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
}

所以,我们必须保证我们要在实现Test接口的子类中定义符合以上要求的测试类,否则,框架将不会运行我们的测试方法。

同时我们也看到,当我们将Test类传入TestSuite后,TestSuite将所有的test构造为TestCase实例,每个实例都会有一个名字,和要测试的方法。

//createTest  (addTest(createTest(theClass, name))时调用)
static public Test createTest(Class theClass, String name)
{
    Constructor constructor;
    try
    {
        constructor = getTestConstructor(theClass);
    }
    catch (NoSuchMethodException e)
    {
        return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
    }
    Object test;
    try
    {
        if (constructor.getParameterTypes()
            .length == 0)
        {
            test = constructor.newInstance(new Object[0]);
            if (test instanceof TestCase)
                ((TestCase) test)
                .setName(name);
        }
        else
        {
            test = constructor.newInstance(new Object[]
            {
                name
            });
        }
    }
    catch (InstantiationException e)
    {
        return (warning("Cannot instantiate test case: " + name + " (" + exceptionToString(e) + ")"));
    }
    return (Test) test;
}

在createTest(Class theClass, String name)方法中theClass是实现了Test接口的类,一般情况下是TestCase的子类。这样name就是TestCase的名字,用于标志一个测试。

在TestCase的protected void runTest() throws Throwable 方法中:

runMethod = getClass()
    .getMethod(fName, null); //获取方法名
runMethod.invoke(this, new Class[0]); //运行测试方法。

4)、测试开始

  Test.run( TestResult) 是实际上调用TestResult.run( Test)。
  //TestResult.run(Test)
  protected void run(final TestCase test)
  {
      startTest(test);
      Protectable p = new Protectable()
      {
          public void protect() throws Throwable
          {
              test.runBare();
          }
      };
      runProtected(test, p);
      endTest(test);
  }

这个方法也是一个模版方法, 关键是runProtected方法:

public void runProtected(final Test test, Protectable p)
{
    try
    {
        p.protect();
    }
    catch (AssertionFailedError e)
    {
        addFailure(test, e);
    }
    catch (ThreadDeath e)
    { // don't catch ThreadDeath by accident
        throw e;
    }
    catch (Throwable e)
    {
        addError(test, e);
    }
}

这个方法起到的作用就是当测试出现异常或错误时会在TestResult中记录,如果是ThreadDeath 就继续抛出异常,程序可能会终止,如果是其他错误和异常,程序将继续运行。这也就是protect的含义。因此,我们抛出Assert中的异常时,不会影响下面的测试继续运行。

然在这个方法中传入的Protectable p的protect方法实际是调用了test.runBare();我们看到,TestRunner调用了Test的run(TestResult)方法后,TestResult实际上是在一个保护的环境下调用TestCase的runBase方法。也就是说如果我们自己的测试类没有继承TestCase,也要定义一个runBase方法,执行基本的操作。基于这一点,我们的测试类应该继承自TestCase。

以上就是关于Junit框架原理及如何使用的所有内容了,你了解了吗?还想知道更多java常见问题及解决方法的话,就请持续奇Q工具网吧。

推荐阅读:

java junit单元测试实例,junit单元测试例子

junit单元测试idea,idea使用junit4单元测试实例

junit单元测试步骤都有哪些?使用idea进行junit单元测试