Sunday, February 14, 2016

Junit Code



https://www.javacodegeeks.com/2013/02/testing-expected-exceptions-with-junit-rules.html
@Rule
11  public ExpectedException exception = ExpectedException.none();
12 
13  @Test
14  public void testExpectedException() {
15    exception.expect(IllegalArgumentException.class);
16    exception.expectMessage(containsString('Invalid age'));
17    new Person('Joe', -1);
18  }

@Test(expected = IllegalArgumentException.class)
1@Test
2public void testExpectedException3() {
3  try {
4    new Person('Joe', -1);
5    fail('Should have thrown an IllegalArgumentException because age is invalid!');
6  catch (IllegalArgumentException e) {
7    assertThat(e.getMessage(), containsString('Invalid age'));
8  }
9}
http://dtucker.co.uk/hack/data-driven-restful-api-testing-for-java.html
https://github.com/junit-team/junit4/wiki/Parameterized-tests
The custom runner Parameterized implements parameterized tests. When running a parameterized test class, instances are created for the cross-product of the test methods and the test data elements.
It is also possible to inject data values directly into fields without needing a constructor using the @Parameter annotation, like so:
@RunWith(Parameterized.class)
public class FibonacciTest {
    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                 { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }  
           });
    }

    @Parameter // first data value (0) is default
    public /* NOT private */ int fInput;

    @Parameter(value = 1)
    public /* NOT private */ int fExpected;

    @Test
    public void test() {
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

In order to easily identify the individual test cases in a Parameterized test, you may provide a name using the @Parameters annotation. This name is allowed to contain placeholders that are replaced at runtime:
  • {index}: the current parameter index
  • {0}, {1}, …: the first, second, and so on, parameter value. NOTE: single quotes ' should be escaped as two single quotes ''.
    @Parameters(name = "{index}: fib({0})={1}")
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][] { 
                 { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
           });
    }
http://www.tutorialspoint.com/junit/junit_parameterized_test.htm
http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/
@RunWith(value = Parameterized.class)
public class MathUtilsTest {
 private int numberA;
 private int numberB;
 private int expected;
 //parameters pass via this constructor
 public MathUtilsTest(int numberA, int numberB, int expected) {
  this.numberA = numberA;
  this.numberB = numberB;
  this.expected = expected;
 }
 //Declares parameters here
 @Parameters(name = "{index}: add({0}+{1})={2}")
 public static Iterable<Object[]> data1() {
  return Arrays.asList(new Object[][] { 
   { 1, 1, 2 }, 
   { 2, 2, 4 }, 
   { 8, 2, 10 }, 
   { 4, 5, 9 } 
  });
 }
 @Test
 public void test_add() { 
  assertEquals(expected,MathUtils.add(numberA, numberB));
 }
}
Pragmatic Unit Testing in Java 8 with JUnit
The Value of Focused, Single-Purpose Tests

The cooler, more descriptive names all follow the form:
doingSomeOperationGeneratesSomeResult

You might also use a slightly different form such as:
someResultOccursUnderSomeCondition

Good Tests Are FIRST:
[F]ast
[I]solated
[R]epeatable
[S]elf-validating
[T]imely

What to Test: The Right-BICEP
Right

Are the results right?
B
Are all the boundary conditions correct?
I
Can you check inverse relationships?

C
Can you cross-check results using other means?
assertThat(Newton.squareRoot(1969.0),​
    closeTo(​Math​.sqrt(1969.0), Newton.TOLERANCE));

E
Can you force error conditions to happen?

P
Are performance characteristics within bounds?
http://media.pragprog.com/titles/utj2/code/iloveyouboss/15/test/iloveyouboss/ProfileTest.java
private long run(int times, Runnable func) {
   long start = System.nanoTime();
   for (int i = 0; i < times; i++)
      func.run();
   long stop = System.nanoTime();
   return (stop - start) / 1000000;
}
long elapsedMs = run(numberOfTimes,
   () -> profile.find(
         a -> a.getQuestion().getClass() == PercentileQuestion.class));
assertTrue(elapsedMs < 1000);

Improve any local-variable names.
Introduce meaningful constants.
Prefer Hamcrest assertions.
Split larger tests into smaller, more-focused tests.
Move test clutter to helper methods and @Before methods.

http://blog.xebia.com/conditionally-ignoring-junit-tests/
The default test runner in JUnit 4 is BlockJUnit4ClassRunner (javadocsource code). From this class we need to override just one method:
1
protected void runChild(FrameworkMethod method, RunNotifier notifier)
This method is run once for each test and checks whether the @Ignore annotation is present.
@Target( ElementType.METHOD )
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemPropertyCondition {
    /** The name of a system property that must be set for the test to run. */
    String value();
}
public class ConditionalTestRunner extends BlockJUnit4ClassRunner {
    public ConditionalTestRunner(Class klass) {
        super(klass);
    }
    @Override
    public void runChild(FrameworkMethod method, RunNotifier notifier) {
        SystemPropertyCondition condition =
            method.getAnnotation(SystemPropertyCondition.class)
        if (condition != null && System.getProperty(condition.value()) != null) {
            super.runChild(method, notifier);
        } else {
            notifier.fireTestIgnored(describeChild(method));
        }
    }
}
@RunWith(ConditionalTestRunner.class)
public class SomeExampleTest {
    @Test
    @SystemPropertyCondition("com.mycompany.includeConditionalTests")
    public void testMethodThatRunsConditionally() {
        // Normal test code goes here
    }
}
if you want to be able to annotate entire classes as well as individual methods, make the annotation target bothElementType.TYPE and ElementType.METHOD and, in your test runner, evaluate not onlymethod.getAnnotations(), but also getTestClass().getAnnotations(). It's not difficult to add extra annotation types that check for things like Host OS, environment variables or an open TCP port on localhost.
http://www.cnblogs.com/yunna/archive/2012/08/22/Yunna.html
用JUnit4进行测试有两种方式分别是:
  (1)、命令行方式:java org.junit.runner.JUnitCore [java class...];
  (2)、程序方式:直接调用org.junit.runner.JUnitCore.runClass(Class<?>...clazz)方法;
这两种测试的方法,最终调用的是同一个执行体。先看第一种测试方法:在JUnitCore这个类中,我们可以发现它有一个main方法:
1 public static void main(String... args) {
2     runMainAndExit(new RealSystem(), args);
3 }
这就是命令行方式执行的入口,JUnitCore.runMain()方法
 1 public Result runMain(JUnitSystem system, String... args) {
 2         system.out().println("JUnit version " + Version.id());
 3         List<Class<?>> classes= new ArrayList<Class<?>>();
 4         List<Failure> missingClasses= new ArrayList<Failure>();
 5         for (String each : args)
 6             try {
 7                 classes.add(Class.forName(each));
 8             } catch (ClassNotFoundException e) {
 9                 system.out().println("Could not find class: " + each);
10                 Description description= Description.createSuiteDescription(each);
11                 Failure failure= new Failure(description, e);
12                 missingClasses.add(failure);
13             }
14         RunListener listener= new TextListener(system);
15         addListener(listener);
16         Result result= run(classes.toArray(new Class[0]));
17         for (Failure each : missingClasses)
18             result.getFailures().add(each);
19         return result;
20     }
再看第二种执行方式,调用的是JUnitCore.runClass(Class<?>... classes),
1     public static Result runClasses(Class<?>... classes) {
2         return new JUnitCore().run(defaultComputer(), classes);
3     }
在这个方法里面,会构造一个默认的Computer(这个后面会解释它的用处),紧接着就调用了JUnitCore的另外一个重载的run()方法
1   public Result run(Computer computer, Class<?>... classes) {
2         return run(Request.classes(computer, classes));
3    }
 1 public Result run(Runner runner) {
 2         Result result= new Result();
 3         RunListener listener= result.createListener();
 4         fNotifier.addFirstListener(listener);
 5         try {
 6             fNotifier.fireTestRunStarted(runner.getDescription());
 7             runner.run(fNotifier);
 8             fNotifier.fireTestRunFinished(result);
 9         } finally {
10             removeListener(listener);
11         }
12         return result;
13     }
该方法才是真正的开始执行Test,首先会构造一个Result的对象,顾名思义就知道它是记录运行时的状态,
 1 public class Result implements Serializable {
 2     private static final long serialVersionUID = 1L;
 3     private AtomicInteger fCount = new AtomicInteger();
 4     private AtomicInteger fIgnoreCount= new AtomicInteger();
 5     private final List<Failure> fFailures= Collections.synchronizedList(new ArrayList<Failure>());
 6     private long fRunTime= 0;
 7     private long fStartTime;
 8     //.......
 9     private class Listener extends RunListener {
10     //.......
11     }
12 }
包括:所运行Test的数量,所忽略Test的数量,开始运行的时间,总运行时间,运行期间出现错误的信息;它里面还包含一个私有内部类,该类的作用就是帮助Result记录状态的。最后会执行Runner的run方法。这时就真正开始执行Test了。
这篇主要讲下JUnit是如何构建、运行Runner 。
JUnit4中是通过Request.classes(Computer computer, Class<?>... classes)来构造Runner的
 1 public static Request classes(Computer computer, Class<?>... classes) {
 2         try {
 3             AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true);
 4             Runner suite= computer.getSuite(builder, classes);
 5             return runner(suite);
 6         } catch (InitializationError e) {
 7             throw new RuntimeException(
 8                     "Bug in saff's brain: Suite constructor, called as above, should always complete");
 9         }
10     }
       在上面这个方法中出现了一个重要的类,就是AllDefaultPossibilitiesBuilder,这个类就是用来选择RunerBuilder,继而选择Runner来执行Test。这个类中有一重要的方法runnerForClass(Class<?> testClass)(代码-1)
该方法用来确定选择RunerBuilder,继而选择Runner来执行Test。默认选择的是junit4Builder();(但是你可以自由选择和自己实现,可以用@RunWith Annotation来标注,这样就会选择annotatedBuilder()来作为RunnerBuilder.)。
在 选择好了RunnerBuilder后,接下就是要构建Runner了,这里是用Computer.getSuite()(代码-2)来获取Suite类型的 Runner(通过类层次关系,你会发现Suiter是继承自ParentRunner的,而ParentRunner是实现了Runner接口的)。
初始化阶段作一些重要的初始化工作,它的入口点在 junit.textui.TestRunner 的 main 方法。该方法首先创建一个 TestRunner 实例aTestRunner。之后 main 函数中主体工作函数为 TestResult r = aTestRunner.start(args) 。

  public static void main(String args[]) {

    TestRunner aTestRunner= new TestRunner();

    try {

      TestResult r= aTestRunner.start(args);

      if (!r.wasSuccessful()) 
        System.exit(FAILURE_EXIT);
      System.exit(SUCCESS_EXIT);
    } catch(Exception e) {
      System.err.println(e.getMessage());
      System.exit(EXCEPTION_EXIT);
    }
  }
  public TestResult start(String args[]) throws Exception {
    String testCase= "";
    String method= "";
    boolean wait= false;

    for (int i= 0; i < args.length; i++) {
      if (args[i].equals("-wait"))
        wait= true;
      else if (args[i].equals("-c"))
        testCase= extractClassName(args[++i]);
      else if (args[i].equals("-m")) {
        String arg= args[++i];
        int lastIndex= arg.lastIndexOf('.');
        testCase= arg.substring(0, lastIndex);
        method= arg.substring(lastIndex + 1);
      } else if (args[i].equals("-v"))
        System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
      else
        testCase= args[i];
    }

    if (testCase.equals(""))
      throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");

    try {
      if (!method.equals("")) 
        return runSingleMethod(testCase, method, wait);
      Test suite= getTest(testCase);
      return doRun(suite, wait);
    } catch (Exception e) {
      throw new Exception("Could not create and run test suite: " + e);
    }
  }

  protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception {
    Class<? extends TestCase> testClass= loadSuiteClass(testCase).asSubclass(TestCase.class);
    Test test= TestSuite.createTest(testClass, method);
    return doRun(test, wait);
  }
当 suite 方法在 test case 中定义时,JUnit 创建一个显式的 test suite,它利用 Java 语言的 Reflection 机制找出名为SUITE_METHODNAME的方法,也即 suite 方法:
suiteMethod = testClass.getMethod(SUITE_METHODNAME, new Class[0]);
若 JUnit 无法找到 siute 方法,则抛出异常,流程进入情况 B 代码;若找到,则对用户提供的 suite 方法进行外部特征检验,判断是否为类方法。最后,JUnit 自动调用该方法,构造用户指定的 TestSuite:
test = (Test)suiteMethod.invoke(null, (Object[]) new Class[0]);
TestSuite 采用了Composite 设计模式。在该模式下,可以将 TestSuite 比作一棵树,树中可以包含子树(其它 TestSuite),也可以包含叶子 (TestCase),以此向下递归,直到底层全部落实到叶子为止。 JUnit 采用 Composite 模式维护测试集合的内部结构,使得所有分散的 TestCase 能够统一集中到一个或若干个 TestSuite 中,同类的 TestCase 在树中占据同等的位置,便于统一运行处理。另外,采用这种结构使测试集合获得了无限的扩充性,不需要重新构造测试集合,就能使新的 TestCase 不断加入到集合中。
在 TestSuite 类的代码中,可以找到:
private Vector fTests = new Vector(10);
此即为内部维护的“子树或树叶”的列表。
红框内的代码完成提取整个类继承体系上的测试方法的提取。循环语句由 Class 类型的实例 theClass 开始,逐级向父类的继承结构追溯,直到顶级 Object 类,并将沿途各级父类中所有合法的 testXXX() 方法都加入到 TestSuite 中。
合法 testXXX 的判断工作由:
addTestMethod(methods[i], names, theClass)
完成,实际上该方法还把判断成功的方法转化为 TestCase 对象,并加入到 TestSuite 中。代码如下图 :
Command 模式将调用操作的对象与如何实现该操作的对象解耦。在运行时,TestCase 或 TestSuite 被当作 Test 命令对象,可以像一般对象那样进行操作和扩展,也可以在实现 Composite 模式时将多个命令复合成一个命令。另外,增加新的命令十分容易,隔离了现有类的影响,今后,也可以与备忘录模式结合,实现 undo 等高级功能。
加入 TestSuite 的 TestCase 由 createTest(theClass, name) 方法创建
TestSuite 和 TestCase 都有一个fName实例变量,是在其后的测试运行及结果返回阶段中该 Test 的唯一标识,对 TestCase 来说,一般也是要测试的方法名。在 createTest 方法中,测试方法被转化成一个 TestCase 实例,并通过:
((TestCase) test).setName(name);
用该方法名标识 TestCase 。其中,test 对象也是通过 Refection 机制,通过 theClass 构建的:
test = constructor.newInstance(new Object[0]);
注意:theClass 是图 8 中 getTest 方法的 suiteClassName 字符串所构造的 Class 类实例,而后者其实是命令行参数传入的带测试类 Calculator,它继承了 TestCase 方法。因此,theClass 完全具备转化的条件。

TestCase
  protected void runTest() throws Throwable {
    assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
    Method runMethod= null;
    try {
      // use getMethod to get all public inherited
      // methods. getDeclaredMethods returns all
      // methods of this class but excludes the
      // inherited ones.
      runMethod= getClass().getMethod(fName, (Class[])null);
    } catch (NoSuchMethodException e) {
      fail("Method \""+fName+"\" not found");
    }
    if (!Modifier.isPublic(runMethod.getModifiers())) {
      fail("Method \""+fName+"\" should be public");
    }

    try {
      runMethod.invoke(this);
    }
    catch (InvocationTargetException e) {
      e.fillInStackTrace();
      throw e.getTargetException();
    }
    catch (IllegalAccessException e) {
      e.fillInStackTrace();
      throw e;
    }
  }

TestResult
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);
    }
}
public synchronized void addFailure(Test test, AssertionFailedError e) {
    fFailures.add(new TestFailure(test, e));
    for (TestListener each : cloneListeners()) {
        each.addFailure(test, e);
    }
}
JUnit 采用 Observer 设计模式使得 TestResult 与众多测试结果监听器通过接口 TestListenner 达到松耦合,使 JUnit 可以支持不同的使用方式。目标对象(TestResult)不必关心有多少对象对自身注册,它只是根据列表通知所有观察者。因此,TestResult 不用更改自身代码,而轻易地支持了类似于 ResultPrinter 这种监听器的无限扩充。目前,已有文本界面、图形界面和 Eclipse 集成组件三种监听器,用户完全可以开发符合接口的更强大的监听器。
出于安全考虑,cloneListeners() 使用克隆机制取出监听器列表:
private synchronized Vector cloneListeners() {
    return (Vector)fListeners.clone();
}
TestResult 的 addFailure 进一步调用 ResultPrinter 的 addFailure:


Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts