JUnit Testing

Unit Testing:
  Here Unit indicates a Part of a Project which can be a task/story.Once code is completed by Developer, then That part must be tested.

  Unit Testing = testing one part of project developed by Programmer.


 JUnit 5 : JUnit is a Unit Test framework. It is Opensource Java Library.

=> To test our code (class), we need to define one class that is called asTest Case (Test class).

 To define one Test case we should use
  a. JUnit 5 Annotations  
  b. JUnit 5 Assert API

=> Here, JUnit Runtime is provided with 3 components and one platform runtime.
   i. JUnit Jupiter Engine (JUnit 5 API)
  ii. JUnit Vintage Engine (JUnit 4 and 3 APIs)
  iii. Integration (TestNg, Mock...etc)

=> For Our Test cases (Class + method) object creation and test-method calling done by JUnit Platform Runtime.

1. Open pom.xml , provide Java version and JUnit Dependencies
   

==Example Code#1============================
package in.nit.test;

import org.junit.jupiter.api.Test;

// Test case: A class that will test our code

public class TestEmployee {

    //test methods 
    @Test
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }
    
    @Test
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
    @Test
    public void testDelete() {
        System.out.println("HELLO-DELETE");
    }
}

======================================================
@TestMethodOrder : We can define multiple test methods inside Testcase.Those are executed in Random order by default.

We can specify our own order using @TestMethodOrder + OrderAnnotation Here we need to provide @Order(number).

---Example-------------
package in.nit.test;

import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

// Test case: A class that will test our code
@TestMethodOrder(OrderAnnotation.class)
public class TestEmployee {

    //test methods 
    @Test
    @Order(1)
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }
    
    @Test
    @Order(2)
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
    @Test
    @Order(3)
    public void testDelete() {
        System.out.println("HELLO-DELETE");
    }
}


*) We can use @TestMethodOrder(Alphanumeric.class) for provide test method order.First sort using 0-9 if same found then compare with A-Z sorting order.


-_Example#2_-
package in.nit.test;

import org.junit.jupiter.api.MethodOrderer.Alphanumeric;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

// Test case: A class that will test our code
@TestMethodOrder(Alphanumeric.class)
public class TestEmployee {

    //test methods 
    @Test
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }
    
    @Test
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
    @Test
    public void testDelete() {
        System.out.println("HELLO-DELETE");
    }
}
==============================================================
@BeforeEach : To execute any logic once per test method before starting test method.
@AfterEach  : To execute any logic once per test method after finishing test method.

@BeforeAll : To execute any logic once per test case before starting.
@AfterAll  : To execute any logic once per test case after finishing.

--Example#3----
package in.nit.test;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class TestEmployee {
    
    @BeforeAll
    public static void setupOnce() {
        System.out.println("FROM ONETIME SETUP");
    }

    @BeforeEach
    public void setup() {
        //setup, init data
        System.out.println("FROM SETUP");
    }
    
    @Test
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }

    @Test
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
    @AfterEach
    public void clear() {
        System.out.println("CLEAR DATA");
    }
    
    @AfterAll
    public static void closeAll() {
        System.out.println("FROM ONETIME AT END");
    }

}
Output:
FROM ONETIME SETUP
FROM SETUP
HELLO-SAVE
CLEAR DATA

FROM SETUP
HELLO-UPDATE
CLEAR DATA
FROM ONETIME AT END


*) @DisplayName : This annotation is used to provide 'Readable text' inplace of actual method and class names at JUnit console.

--Ex#4--
package in.nit.test;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("TESTING EMPLOYEE TASK")
public class TestEmployee {
    
    @BeforeAll
    public static void setupOnce() {
        System.out.println("FROM ONETIME SETUP");
    }

    @BeforeEach
    public void setup() {
        //setup, init data
        System.out.println("FROM SETUP");
    }
    
    @Test
    @DisplayName("TESTING SAVE METHOD")
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }

    @DisplayName("TESTING UPDATE METHOD")
    @Test
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
    @AfterEach
    public void clear() {
        System.out.println("CLEAR DATA");
    }
    
    @AfterAll
    public static void closeAll() {
        System.out.println("ONCE AT END");
    }
    
}


*) @Disabled : This annotation is used to specify Ignore one Test-method  while executing test-case (do not execute test method)
           
--Ex#5--
package in.nit.test;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("TESTING EMPLOYEE TASK")
public class TestEmployee {

    @Test
    @DisplayName("TESTING SAVE METHOD")
    public void testSave() {
        System.out.println("HELLO-SAVE");
    }

    @DisplayName("TESTING UPDATE METHOD")
    @Test
    @Disabled
    public void testUpdate() {
        System.out.println("HELLO-UPDATE");
    }
    
}

-----------------------------------------------------------------------
*) @RepeatedTest and TestInfo

=> To execute any test method multiple time (like batch processing) using @RepeatedTest annotation.

 Ex: Export 1-10 Records
       Export 11-20 Records
       Export 21-30 Records

=> To know our Test case details like classname,method name, display name,tag name etc we can use one interface TestInfo.

--Ex#6---
package in.nit.test;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.TestInfo;

@DisplayName("TESTING EMPLOYEE TASK")
public class TestEmployee {
    @RepeatedTest(value = 3,name="{displayName} - {currentRepetition}/{totalRepetitions}")
    @DisplayName("MULTIPLE TEST")
    public void testMultiple(TestInfo info) {
        //System.out.println("HELLO:"+info.getTestClass().get().getName());
        //System.out.println("HELLO:"+info.getTestMethod().get().getName());
        System.out.println("HELLO:"+info.getDisplayName());
    }    
}

==========================================================================
@Tag : These are used to filter test methods for execution in different environments.

For Example, i want to test export example in production env at same
                       i want to test delete operation only in development environment 
then use tag concept and maven-surefire-plugin in pom.xml


---EX#7---
package in.nit.test;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

public class TestEmployee {
    @Test
    @Tag("dev")
    public void testA() {
        System.out.println("HELLO-TEST-A");
    }
    
    @Test
    @Tag("prod")
    public void testB() {
        System.out.println("HELLO-TEST-B");
    }
    
    @Test
    @Tag("prod")
    public void testC() {
        System.out.println("HELLO-TEST-C");
    }
    
    @Test
    @Tag("dev")
    public void testD() {
        System.out.println("HELLO-TEST-D");
    }   
}


pom.xml
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0
    int.nit
    JUnit5App
    1.0

   
        1.8
        1.8
   

   
       
            org.junit.jupiter
            junit-jupiter-engine
            5.6.2
            test
       
   

   
       
           
                org.apache.maven.plugins
                maven-surefire-plugin
                3.0.0-M5
               
                   
                    prod
                   
                    dev
               
           
       
   

 

=> Right click on Project > Run as > maven test.

(or)
=> Right click on code > Run as > Run Configuration
> Click on configuration

 

Assert API : 
 It is used to validate Test, IS CURRENT TEST PASS/FAIL? Expected Value is compared with Actual Result.

=> JUnit 5 has provided class : Assertions (org.junit.jupiter.api) which contains all static method "assert methods".

=> assert methods are used to compare Expected values with Actual Result.If matching TEST PASS, else TEST FAIL.

=> Ex assert method : assertEquals(expected, actual)
    This method is used to compared expected value with actual value
------------------------Code---------------------------
1. Create one Maven simple project

2. pom.xml 
   
        1.8
        1.8
   

   
       
            org.junit.jupiter
            junit-jupiter-engine
            5.6.2
            test
       
   

3. Define one class under src/main/java which needs testing

package in.nit.service;
public class Message {

    public String showMsg(String name) {
        return "Welcome to: Mr/Mrs/Ms "+name;
    }
}

4. TestCase under src/test/java

package in.nit.test;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import in.nit.service.Message;

public class TestMessage {

    // Declare variables
    private Message m;
    private String expected;
    private String actual;

    //provide init-data 
    @BeforeEach
    public void setup() {
        m = new Message();
        expected = "Welcome to :SAM";
        actual = "";
    }

    //Do Unit Test
    @Test
    public void testShowMsg() {
        actual = m.showMsg("SAM");
        assertEquals(expected, actual,"Data may not be matching!");
    }

    //clear heap data /clear memory.
    @AfterEach
    public void clean() {
        m = null;
        expected = actual = null;
    }

}
------------------------------------------------------------------------------

*) assertNotNull() / assertNull()

assertNotNull(object):
   This method is used to specify that given object is not a null value
   it holds data some data, else TEST FAIL.

assertNull(object): it indicates given object is null, else TEST FAIL.


*) assertDoesNotThrow(Executable) : 
   This is used to specify that our method call is not throwing any exception, else if it throwing then TEST FAIL.

--Code---------------
1. Project code

package in.nit.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DbConnection {

    public Connection getCon() throws ClassNotFoundException, SQLException {
        Connection con=null;
        Class.forName("com.mysql.jdbc.Driver");
        con=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");

        return con;
    }
}

2. Unit Test code
package in.nit.test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.sql.Connection;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import in.nit.util.DbConnection;

public class TestDbConnection {

    private DbConnection dbc;
    private Connection con;

    @BeforeEach
    public void initData() {
        dbc=new DbConnection();
    }

    @Test
    public void testGetCon() {
        assertDoesNotThrow(()->{
            con=dbc.getCon();
        });
        assertNotNull(con,"Connection is Not created, Please check!");
    }

    @AfterEach
    public void clean() {
        dbc = null;
        con = null;
    }

}
-----------------------------------------------------------
*) assertSame(ob1,ob2): This method is used to test that GIVEN TWO REFERENCES are POINTED TO ONE OBJECT?
  If yes TEST PASS, else TEST FAIL.

Q) What is the diff b/w assertSame() and assertEquals()?
A) assertEquals () : compares two objects data
     assertSame()  : compares object references.

*) fail() : This is used for testing multiple conditions,  if they are not met Manually FAIL TEST CASE.

--Example: Testing Singleton DB Connections---
1. pom.xml
   
        1.8
        1.8
   

   
       
            org.junit.jupiter
            junit-jupiter-engine
            5.6.2
            test
       
       
            mysql
            mysql-connector-java
            5.1.47
       


2. Project code

package in.nit.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DbConUtil {

    private static Connection con;
    
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getCon() {
        return con;
    }
    
    
}

3. test case

package in.nit.test;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.fail;

import java.sql.Connection;

import org.junit.jupiter.api.Test;

import in.nit.util.DbConUtil;

public class TestDbConUtil {

    @Test
    public void testGetCon() {
        Connection con1=DbConUtil.getCon();
        Connection con2=DbConUtil.getCon();

        //assertNotNull(con1,"Connection not created..");
        if(con1==null || con2==null ) {
            //TEST CASE IS FAILED
            fail("CONNECTIONS ARE NOT CRATED..");
        }
        assertSame(con1, con2,"May not be Same Connection!");
    }
}

------------------------------------------------------------
*) assertArrayEquals() :use this method to compare data of two arrays (expected, actual)

Test case:
package in.nit.test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import org.junit.jupiter.api.Test;
public class TestSample {

    @Test
    public void testNormal() {
        int expected[] = {10,20,30};
        int actual[] = {10,20};
        assertArrayEquals(expected, actual,"Data may not be same in two arrays!");
    }

}


*)assertTrue()/assertFalse()
  These methods are used to test one boolean condition/expression/value.

  assertTrue(): boolean value is expected as TRUE, else TEST FAIL.
  assertFalse(): boolean value is expected as FALSE, else TEST FAIL.

--Example--
package in.nit.test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class TestSample {

    @Test
    public void testNormal() {
        boolean exist = false;
        assertTrue(exist,"Data may not exist!");
    }
}


*) assertThrows(): Expecting that our logic thorws one expcetion as : T (Type)
  assertThrows(ExpectedExceptionType.class, ()-> {our logic});


-_Ex_-
package in.nit.test;

import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

public class TestSample {

    @Test
    public void testNormal() {
        assertThrows(NullPointerException.class, ()->{
            throw new ArrayIndexOutOfBoundsException();
        });
        
    }

}

-----------------------------
assertAll(Executable...) : This is used to group all asset test methods and execute once.If all are PASS then TEST IS PASS, If one FAIL then TEST IS FAIL.

package in.nit.test;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.fail;

import java.sql.Connection;
import org.junit.jupiter.api.Test;
import in.nit.util.DbConUtil;
public class TestSample {

    @Test
    public void testNormal() {
        assertAll(
                ()->{
                    assertNotNull(DbConUtil.getCon());
                },
                ()->{
                    Connection con1,con2;
                    con1=DbConUtil.getCon();
                    con2=DbConUtil.getCon();
                    assertSame(con1, con2);
                },
                ()->{
                    Connection con1,con2;
                    con1=DbConUtil.getCon();
                    con2=DbConUtil.getCon();
                    if(con1==null && con2==null) {
                        fail();
                    }
                }
                );
    }

}