Sunday 7 October 2018

Using Verify statements instead of Assert in Test NG

Using Verify statements instead of Assert in Test NG


Often we see, when we use Assert statement of TestNG... if test fails it terminates there and starts new test.
If we have requirement to continue executing test even verification failed (marking overall result as failed), then we can use Verify statements.  It can be accomplished as follows. 

First create a Class - TestMethodErrorBuffer with following methods. 

Mainly to get, set and remove testErrorBuffer. 



// thread safe while running tests in parallel
    private static ThreadLocal<List<Throwable>> testErrorBuffer = 
new ThreadLocal<List<Throwable>>();
     
    static List<Throwable> get(){
        return testErrorBuffer.get();
    }
     
    static void set(List<Throwable> errorBuffer){
        testErrorBuffer.set(errorBuffer);
    }
     
    static void remove(){
        testErrorBuffer.remove();
    }

Secondly create a class called "Verify" - define all methods related to Verify. 
Note here: for every method for eg:- verifyTrue() we are internally calling Assert method 
inside try catch block and if there is a exception we are adding it to ErrorBuffer. 

import org.testng.Assert;

public class Verify {
 
    protected Verify() {
        // hide constructor
    }
 
    static public void verifyTrue(boolean condition, String message) {        
        try{
            Assert.assertTrue(condition, message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }         
    }
 
    static public void verifyFalse(boolean condition, String message) {
        try{
            Assert.assertFalse(condition, message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
 
    static public void verifyEquals(Object actual, Object expected, String message) {
        try{
            Assert.assertEquals(actual, expected, message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
 
    public static void verifyNotSame(Object actual1, Object actual2, String message) {
        try{
            Assert.assertNotSame(actual1,actual2,message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
    
    public static void verifySame(Object actual1, Object actual2, String message) {
        try{
            Assert.assertSame(actual1,actual2,message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
    
    public static void verifyNotNull(Object object, String message) {
        try{
            Assert.assertNotNull(object,message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
    
    public static void verifyNull(Object object,  String message) {
        try{
            Assert.assertNull(object,message);
        }catch(AssertionError e){
            addToErrorBuffer(e);
        }
    }
 
    
 
    private static void addToErrorBuffer(AssertionError e){   
 
        try{                
 
            VerificationError verificationError = new VerificationError(e.getMessage());
 
            verificationError.setStackTrace(e.getStackTrace());
 
            TestMethodErrorBuffer.get().add(verificationError);
 
        }catch(NullPointerException ex){
 
            throw new RuntimeException("Exception at " + TestMethodListener.class.getName() );
        }
 
    }
}

Thirdly create a class called TestMethodListener implements IInvokedMethodListener
Here, in beforeInvocation we are going to define new ErrorBuffer for every test method. 
in afterInvocation set result and append all the results obtained from verify failures. 


import java.util.ArrayList;
import java.util.List;
 
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;
import org.testng.internal.Utils;
 
public class TestMethodListener implements IInvokedMethodListener{
 
    
    public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
 
        if(method.isTestMethod()){  
             
            if(TestMethodErrorBuffer.get()!=null){
                throw new RuntimeException("Stale error buffer detected!");
            }
             
            TestMethodErrorBuffer.set(new ArrayList<Throwable>()); 
// each test method will have its own error buffer
        }
 
    }
 
   
    public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
 
        if(method.isTestMethod()){
 
            List<Throwable> lThrowable = TestMethodErrorBuffer.get();
 
            /* if there are verification failures */
            if(lThrowable.size() > 0){
 
                /* set test result to failure */
                testResult.setStatus(ITestResult.FAILURE);
 
                /* if there is assertion error/exception then add it to throwable list */
                if(testResult.getThrowable() != null){
                    lThrowable.add(testResult.getThrowable());
                }
 
                int size = lThrowable.size();
 
                /* if there is only one throwable then set it directly to test result */
                if(size == 1){
                    testResult.setThrowable(lThrowable.get(0));
                }else{
 
                    StringBuffer failureMessage = new StringBuffer("Multiple failures (")
.append(size).append(")\n");
                    StringBuffer fullStack = new StringBuffer();
 
                    for(int i =0 ; i < size-1; i++){ 
                        failureMessage.append("(").append(i+1).append(")")
.append(lThrowable.get(i).getClass().getName()).append(":").append(lThrowable.get(i).
getMessage()).append("\n");                      
                        fullStack.append("Failure ").append(i+1).append(" of ").
append(size).append("\n");  
                        fullStack.append(Utils.stackTrace(lThrowable.get(i),false)[1]).
append("\n");
                    }
 
                    fullStack.append("Failure ").append(size).append(" of ").
append(size).append("\n");
                    Throwable last = lThrowable.get(size-1);                    
                    failureMessage.append("(").append(size).append(")").
append(last.getClass().getName()).append(":").append(last.getMessage()).append("\n\n");
                     
                    fullStack.append(last.toString());
 
                    testResult.setThrowable(new Throwable(failureMessage.toString() + 
fullStack.toString()));
                    testResult.getThrowable().setStackTrace(last.getStackTrace());
                }
 
            }
             
            TestMethodErrorBuffer.remove(); // remove stale
             
        }
    }
 
}

Finally create a class called VerificationError  which extends Error 


public class VerificationError extends Error{
 
    private static final long serialVersionUID = 8247563849457669512L;
 
    public VerificationError(String message){
        super(message);
    }
     
}


Now you can use TestMethodListener in Test NG class by providing @Listeners annotation 
or in Test NG XML

@Listeners(TestMethodListener.class)
public class TestNG {
 
 @Test
 public void test1()
 {
  Verify.verifyFalse(true, "msg1");
  
  Verify.verifyEquals("tes1", "test2", "test2 doesn't match"); 
 }
 
 @Test
 public void test2()
 {
  Verify.verifyFalse(true, "msg2");
  
  Verify.verifyEquals("tes3", "test4", "test4 doesn't match"); 
 }

}

This will be the sample output for above test class. 

FAILED: test1
java.lang.Throwable: Multiple failures (2)
(1)listeners.VerificationError:msg1 expected [false] but found [true]
(2)listeners.VerificationError:test2 doesn't match expected [test2] but found [tes1]

FAILED: test2
java.lang.Throwable: Multiple failures (2)
(1)listeners.VerificationError:msg2 expected [false] but found [true]
(2)listeners.VerificationError:test4 doesn't match expected [test4] but found [tes3]

Note in the output, even though one verification got failed from verify other verify statement also 
got executed unlike Assert. And finally result "failed" is set on Test method. 

Hope this blog has been useful to you. If yes do follow my blogs by entering your email by 
clicking on "Follow" (blue) button. 

You can as well explore my 

Youtube Channel: https://www.youtube.com/user/srinivaskinik

And 

facebook page: https://www.facebook.com/srinivaskinikalmady/




No comments:

Post a Comment

How to schedule RFT (Rational Functional Tester) scripts to run using Jenkins / schedule

How to schedule RFT (Rational Functional Tester) scripts to run using Jenkins / schedule 1. Create a batch file with following content ...