Detailed Junit Unit Test Framework for JAVA Automation

Keywords: Java Junit calculator Maven

Detailed Junit Unit Test Framework for JAVA Automation

1. Overview of JUnit & Configuration
1. What is Junit?

Junit is an open source test framework for the Java programming language for writing and running tests.Official address: https://junit.org/junit4/

2. Maven Configuration

?xml version="1.0" encoding="UTF-8"?>

     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>junit</groupId>
<artifactId>junitTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

Assertions Assertions
JUnit provides some helpful functions to determine whether the method under test is performing as well as we expect.These auxiliary functions are called assertions.
Common assertions:

Method Sample Function
AssertArrayEquals assertArrayEquals ("message", expected, actual); determines whether two arrays are equal
AssertEquals assertEquals ("message", "text", "text"); determines whether two objects are equal
AssertFalse assertFalse ("failure - should be false", false); determines if the expression is false
TestAssertTrue assertTrue ('failure - should be true', true); determines if the expression is true
AssertNotNull assertNotNull ("should not be null", new Object ())); determine if it is not empty
AssertNull assertNull ("should be null"; determines whether it is empty
AssertNotSame assertNotSame ("should not be the same Object", new Object (), new Object ())); determine if it is a different object
AssertSame assertSame ("should be same", aNumber, aNumber); determines if the same object
...... ...... ......

3. Test Runners Test Runner
All test methods in JUnit are executed by the test runner.When a class is annotated by @RunWith or extends a class annotated by @RunWith, JUnit will use the referenced class to perform the test instead of using the JUnit's built-in runner.

org.junit.runner.JUnitCore.runClasses(TestClass1.class, ...);

Specialized Runners:
Suite: Suite is a standard runner that allows you to manually build test sets containing many classes.
Parameterized:Parameterized is a standard runner that implements parameterized testing.When running parameterized test classes, test methods and test data are merged to create test instances.
Categories: The Categories runner defines classifications, defining whether tests are included or excluded.

IV. Aggregating tests in suites Suite
The test suite bundles several unit test cases and executes them together, using the @RunWith and @Suite annotations.

@RunWith(Suite.class)
@Suite.SuiteClasses({AssertTests.class, CalculatorTest.class})
public class SuiteTest {

// the class remains empty, used only as a holder for the above annotations

}

5. Test execution order execution sequence
To change the order in which tests are executed, simply use the @FixMethodOrder annotation on the test class and specify an available MethodSorter:
@FixMethodOrder(MethodSorters.DEFAULT):JUnit uses a deterministic, but unpredictable order by default
@FixMethodOrder(MethodSorters.JVM): Keep the execution order of the test methods in the order returned by the JVM, and the execution order of each test may be different
@FixMethodOrder(MethodSorters.NAME_ASCENDING): Sorts by method name of the test method, according to dictionary collation (ASC increases from small to large)

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ExecutionOrderTest {

@Test
public void testB() {
    System.out.println("second");
}
@Test
public void testA() {
    System.out.println("first");
}
@Test
public void testC() {
    System.out.println("third");
}

}

Run result:

first
second
third

6. Expected Exceptions exception test
Used to test if a method throws the correct exception.
1. @Test(expected=xxx) method: When an exception is thrown that is the same as the exception specified by the expected parameter, the test passes.
2. try...fail...catch...method: Catches the exception information of the specific statement to be tested and asserts that the fail method will be called when no exception is thrown and outputs the information about the failure of the test.
3. ExpectedException Rule: Use the Rule tag to specify an ExpectedException and specify the expected Exception type before testing the corresponding operation.

public class ExpectedExceptionsTest {

//Method 1: @Test(expected=xxx)
@Test(expected = IndexOutOfBoundsException.class)
public void empty() {
    new ArrayList<Object>().get(0);
}

//Method 2: try...fail...catch...When no exception is thrown, the fail method is called to output information about the test failure.
@Test
public void testExceptionMessage() {
    try {
        new ArrayList<Object>().get(0);
        fail("Expected an IndexOutOfBoundsException to be thrown");
    } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
        assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
    }
}

//Method 3: Use the Rule tag to specify an ExpectedException before testing, and specify the expected Exception type (such as IndexOutOfBoundException.class) before testing the corresponding operation
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();
    thrown.expect(IndexOutOfBoundsException.class);
    thrown.expectMessage("Index: 0, Size: 0");
    list.get(0);
}

}

7. Matchers and assertThat
JUnit 4.4 introduces the Hamcrest framework, and Hamcest provides a set of Matcher s that are closer to natural languages, more readable, and more flexible.And with the new assertThat assertion syntax, combined with the matches provided by Hamcrest, all tests can be performed using this method alone.
assertThat syntax:
assertThat(T actual, Matcher matcher);
assertThat(String reason, T actual, Matcher matcher);
Where reason is the output information when an assertion fails, actual is the value or object of the assertion, and matcher is the matcher of the assertion, the logic inside determines that a given actual object is not satisfied with the assertion.
Matchers are detailed in:
http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html

8. Ignoring tests ignore tests
Method annotated with @Ignore will not be executed
After the class is annotated with @Ignore, all test methods below it will not be executed

public class IgnoreTest {

@Ignore("Test is ignored as a demonstration")
@Test
public void testSame() {
    assertThat(1, is(1));
}

}

9. Timeout for Tess Timeout Test
The @Timeout comment is used to test the execution time of a particular method.If the execution time of the test method is greater than the specified timeout parameter, the test method will throw an exception and the test result will be a failure.The specified timeout parameter is in milliseconds.
1. @Test comment timeout parameter, scoped to method, in milliseconds

@Test(timeout = 2000)

public void testSleepForTooLong() throws Exception {
    log += "ran1";
    TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}

2. Timeout Rule, scoped to test class

public class TimeoutTests {

public static String log;
private final CountDownLatch latch = new CountDownLatch(1);

@Rule
public Timeout globalTimeout = Timeout.seconds(3); // 3 seconds max per method tested

@Test
public void testSleepForTooLong() throws Exception {
    log += "ran1";
    TimeUnit.SECONDS.sleep(100); // sleep for 100 seconds
}

@Test
public void testBlockForever() throws Exception {
    log += "ran2";
    latch.await(); // will block
}

}

10. Parameterized tests
Parametric tests allow developers to run the same test repeatedly with different values.Create a parameterized test step:

Annotate the test class with @RunWith(Parameterized.class).
Create a public static method annotated by @Parameters that returns a collection (array) of objects as a collection of test data.
Create a public constructor that accepts test data.
Create an instance variable for each column of test data.
Create test cases using instance variables as the source of test data.
1. Constructor mode

@RunWith(Parameterized.class)
public class FibonacciTest {

@Parameters(name = "{index}: fib({0})={1}")
public static Collection<Object[]> data() {
    return Arrays.asList(new Object[][] {
            { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
    });
}
private int fInput;
private int fExpected;
public FibonacciTest(int input, int expected) {
    this.fInput = input;
    this.fExpected = expected;
}
@Test
public void test() {
    assertEquals(fExpected, Fibonacci.compute(fInput));
}

}

2. Field injection method

@RunWith(Parameterized.class)
public class FibonacciTest {

@Parameters(name = "{index}: fib({0})={1}")
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(1)
public /* NOT private */ int fExpected;
@Test
public void test() {
    assertEquals(fExpected, Fibonacci.compute(fInput));
}

}

11. Assumptions with assume Assumption Test
When using the hypothesis method in the Assumptions class, an error will be reported if the hypothesis is not established, but the test will show ignore execution.That is, when there are more than one test method in a class, one of the hypothetical test methods assumes failure and the other test methods succeed, the test class also shows success.
Assumptions apply when executing code that does not affect the success of the test.

public class AssumptionsTest {

@Test
public void testAssumTrue() {
    System.out.println("test");
    assumeTrue(3>5);
    //All of the following code in this method executes when the above assumptions are true
    //If the above assumption is not valid, the execution of the code below the line is ignored and an error is reported.
    System.out.println("assume is true!");
}
@Test
public void testAssumFalse(){
    assumeFalse(3>5);
    System.out.println("assume is true!");
}

}

The following syntax is supported in JUnit5:

@Test

public void testAssumTrueMessage() {
    assumeTrue(3<5,
            //The second parameter is the output custom error message when the first parameter is not valid
            () -> "Aborting test: not on developer workstation");
    System.out.println("assume is true!");
}
@Test
public void testAssumeTrueLambda(){
    //The first parameter of this method is a functional interface, and the return value of no parameter is boolean
    assumeTrue(()->{
        System.out.println("in assumeTrue");
        boolean flag = false;
        return flag;
    });
    System.out.println("out assumeTrue");
}
@Test
public void testAssumThat() {
    assumingThat(3>5,
            () -> {
                //Unlike the above method, the statement is executed only if the current assumption is true
                //And will only affect the code in that lambda expression
                assertEquals(2, 2);
            });
    //The assertion here is not restricted by assumingThat above and will be executed in all cases
    System.out.println("no effect");
    assertEquals("a string", "a string");
}

Rules Rules
A JUnit Rule is a class that implements TestRule to execute some code before and after the execution of each test method.
1. Rule that comes with the framework
JUnit comes with many JUnit Rule s that have already been implemented well, such as Timeout, ExpectedException, and so on.
2. Customize Rule
Customizing a Rule is implementA TestRule interface that implements a method called apply().
Example: Before the test method runs, record the class name and method name where the test method is located, and print it out after the test method runs.

public class MethodNameExample implements TestRule {

@Override
public Statement apply(final Statement base, final Description description) {
    return new Statement() {
        @Override
        public void evaluate() throws Throwable {
            //Actions done before base.evaluate() for the test method
            String className = description.getClassName();
            String methodName = description.getMethodName();
            //Run Test Method
            base.evaluate();
            //What happens after base.evaluate() for the test method
            System.out.println("Class name: "+className +", method name: "+methodName);
        }
    };
}

}

public class RuleTest2 {

@Rule
public MethodNameExample methodNameExample = new MethodNameExample();
@Test
public void addition_isCorrect() throws Exception {
    assertEquals(4, 2 + 2);
}
@Test
public void mulitiplication_isCorrect() throws Exception {
    assertEquals(4, 2 * 2);
}

}

Thirteen. Theories
In parameterized testing, we need to give all the specific test data groups.In the Theories test, the user only needs to give some data, which JUnit automatically uses to assemble various possible combinations to perform the test.

1. Built-in implementation

(1) @DataPoints Annotate Static Variable Style

@RunWith(Theories.class)
public class TheoryTest {

//Maximum error allowed
private static final double DELTA = 0.01;
/*@DataPoints Annotate Static Variables*/
@DataPoint
public static int ZERO = 0;
@DataPoint
public static int TWO = 2;
@DataPoint
public static int EIGHT = 8;
//Mark this test Theory
@Theory
public void testDivide(int dividend, int divisor) {
    //Skip case with division 0
    assumeThat(divisor, not(0));
    //The Calculator.divide(dividend, divisor) method returns the result of their division
    assertEquals(dividend / divisor, Calculator.divide(dividend, divisor), DELTA);
    System.out.println("Passed with: dividend=" + dividend + ", divisor=" + divisor);
}

}

(2) @DataPoints annotation static method method method

@RunWith(Theories.class)
public class TheoryTest {

//Maximum error allowed
private static final double DELTA = 0.01;
/*@DataPoints Annotate a static method*/
@DataPoints
public static int[] getTestData() {
    return new int[]{0, 2, 8};
}
//Mark this test Theory
@Theory
public void testDivide(int dividend, int divisor) {
    //Skip case with division 0
    assumeThat(divisor, not(0));
    //The Calculator.divide(dividend, divisor) method returns the result of their division
    assertEquals(dividend / divisor, Calculator.divide(dividend, divisor), DELTA);
    System.out.println("Passed with: dividend=" + dividend + ", divisor=" + divisor);
}

}

@DataPoint is used to annotate a static variable (or static method) to indicate that the variable is a data point.When the Theory test, testDivide, is executed, JUnit combines all the DataPoint data into a set of test data and executes the test separately.Executing the above test will output the following results:

Passed with: dividend=0, divisor=2
Passed with: dividend=0, divisor=8
Passed with: dividend=2, divisor=2
Passed with: dividend=2, divisor=8
Passed with: dividend=8, divisor=2
Passed with: dividend=8, divisor=8

(3) If you need to qualify a parameter, you can use the @TestOn annotation

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.experimental.theories.suppliers.TestedOn;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
@RunWith(Theories.class)
public class TheoryTest {

//Maximum error allowed
private static final double DELTA = 0.01;
//If you need to qualify a parameter, you can use the @TestOn annotation
@Theory
public void testDivide2(
        @TestedOn(ints = {0, 2, 8}) int dividend,
        @TestedOn(ints = {2, 8}) int divisor
) {
    assertEquals(dividend / divisor, Calculator.divide(dividend, divisor), DELTA);
    System.out.println("Passed with: dividend=" + dividend + ", divisor=" + divisor);
}

}

2. Custom implementation

JUnit only provides a simple Parameter Supplier implementation of type int by default, but the real value of the Theory mechanism is that it can completely customize reusable Parameters relatively simply by referring to the @TestedOn approachSupplier, a limited range parameter value test scenario adapted to a variety of complex requirements, satisfies the highly dynamic custom range value automation test required by developers while retaining the same strong compatibility as @Test in general.
Example:
(1) Define annotation annotation annotation interface Between

@Retention(RetentionPolicy.RUNTIME)
//Declare the delegate processing class used by the comment interface
@ParametersSuppliedBy(BetweenSupplier.class)
public @interface Between{

// Declare all available parameters with the effect @Between([first = int,] last = int)
int first() default 0;  // Declare defaults
int last();

}

(2) Define the delegate processing class BetweenSupplier

public class BetweenSupplier extends ParameterSupplier {

@Override
public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
    // Custom Argument Value List
    List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
    // Get annotation variables
    Between between = sig.getAnnotation(Between.class);
    // Get the first value passed in through the comment @Between
    int first = between.first();
    // Gets the last value passed in through the comment @Between
    int last = between.last();
    for (int i = first; i <= last; i++) {
        // PotentialAssignment.forValue(String name, Object value)
        // name is a description tag for value, not useful
        // Value is an optional value for an argument
        list.add(PotentialAssignment.forValue("name", i));
    }
    return list;
}

}

(3) Call method

@RunWith(Theories.class)
public class TheoryDefinedTest {

@Theory
public final void test(@Between(last = 0) int i, @Between(first = 3, last= 10) int j) {
    // i is 0 (first default = 0, last=0), and j is 3-10
    System.out.println("i="+i+"  j="+j);
}

}

(4) Running results

i=0 j=3
i=0 j=4
i=0 j=5
i=0 j=6
i=0 j=7
i=0 j=8
i=0 j=9
i=0 j=10

14. Test fixtures
Test Fixture refers to a fixed environment required by a test run, as well as a stable, common and repeatable environment required before the test run. This "environment" can be not only data, but also preparation for the software under test, such as instantiating the classes on which the method under test depends, loading databases, and so on.

@Before - Run before each @Test method
@After - Runs after each @Test method
@BeforeClass - Run once before all @Test methods
@AfterClass - Run once after all @Test parties
Note:
1. If you create a subclass that inherits a parent class with fixture annotations, the @Before method in the subclass will execute before the test method and after the @Before execution of the parent class.
2. If an exception is thrown in the @Before method, @Test method skips, but @After still executes
3. Each test method runs in an instance of a separate test class, @BeforeClass executes before the test instance is created

public class FixtureTest {

private static int quantity = 0;
public FixtureTest() {
    quantity++;
}
@BeforeClass
public static void breforeTestOnlyOnce() throws Exception {
    System.out.println("Run before all test only once..."+ quantity);
}
@AfterClass
public static void afterTestOnlyOnce() throws Exception {
    System.out.println("Run after all test only once..."+ quantity);
}
@Before
public void beforePerTest() {
    System.out.println("Run before per test ..."+ quantity);
}
@After
public void afterPerTest() {
    System.out.println("Run after per test ..."+ quantity);
}
//Test Method
@Test
public void testOne() {
    System.out.println("testOne Start..."+ quantity);
}
@Test
public void testTwo() {
    System.out.println("testTwo Start..."+ quantity);
}

}

Run result:

Run before all test only once...0
Run before per test ...1
testOne Start...1
Run after per test ...1
Run before per test ...2
testTwo Start...2
Run after per test ...2
Run after all test only once...2

Categories Use Case Classification
Comparison of category and testSuite: testSuite is a class-level grouping (xx.class), category is a case-level grouping (@Test), and category is an upgrade of testSuite
category steps:
1. Create test classes and test cases in them
2. Create interfaces: Create interfaces grouped by use cases
3. @Category annotations: Grouping use cases with @Category annotations
4. Create classes to perform the classification of these groups

public interface FastTests { / category marker / }
public interface SlowTests { / category marker / }

public class A {
@Test
public void a() {

fail();

}
@Category(SlowTests.class)
@Test
public void b() {
}
}

@Category({SlowTests.class, FastTests.class})
public class B {
@Test
public void c() {
}
}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b and B.c, but not A.a
}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@ExcludeCategory(FastTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
// Will run A.b, but not A.a or B.c
}

16. Summary
If you find this article helpful, if you are interested in software testing, interface testing, automated testing, and exchange of interview experience, you are welcome to join the Software Testing Technology Group: 695458161, the free materials distributed by the group are the essence of my more than 10 years'testing career.There are also peer gods to exchange technology.

Walk alone, walk far!Welcome to the Software Test Technology Exchange Group: 695458161 various software test data, WEB automation, interface automation, APP automation, jenkins continuous integration under not too cool!You can also receive advanced test data free of charge regularly.
Author: Zhuge
Original Address https://www.cnblogs.com/csmashang/p/12696717.html

Posted by phplearner on Tue, 14 Apr 2020 18:58:28 -0700