Strings are very useful and they can contain sequences of characters and there are a lot of methods associated with Strings. Moreover, encoding and decoding are possible for a given use case and it can be validated whether are they equal or not for a given requirement. They can be tested for validity with JUnit as well. Given requirement for
encode:
- Convert all the characters of the given string to Uppercase
- Check for the first occurrence of ‘,’ and replace that with ‘.’
- Similarly the last occurrence of ‘!’ and replace that with ‘.’
decode:
Bring back the original test string
Note:
For specific requirements, the code is written. Our task is to write the business logic and test the same with JUnit.
Let’s do that via a Maven project that contains the dependency for JUnit.
Example Maven Project
Project Structure:
pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < groupId >com.gfg.StringServicesJava</ groupId > < artifactId >StringServicesJava</ artifactId > < packaging >jar</ packaging > < version >1.0-SNAPSHOT</ version > < properties > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > < maven.compiler.source >1.8</ maven.compiler.source > < maven.compiler.target >1.8</ maven.compiler.target > < junit.version >5.3.1</ junit.version > < pitest.version >1.4.3</ pitest.version > </ properties > < dependencies > <!-- junit 5, unit test --> < dependency > < groupId >org.junit.jupiter</ groupId > < artifactId >junit-jupiter-engine</ artifactId > < version >${junit.version}</ version > < scope >test</ scope > </ dependency > </ dependencies > < build > < finalName >maven-mutation-testing</ finalName > < plugins > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-surefire-plugin</ artifactId > < version >3.0.0-M1</ version > </ plugin > < plugin > < groupId >org.pitest</ groupId > < artifactId >pitest-maven</ artifactId > < version >${pitest.version}</ version > < executions > < execution > < id >pit-report</ id > < phase >test</ phase > < goals > < goal >mutationCoverage</ goal > </ goals > </ execution > </ executions > <!-- Need this to support JUnit 5 --> < dependencies > < dependency > < groupId >org.pitest</ groupId > < artifactId >pitest-junit5-plugin</ artifactId > < version >0.8</ version > </ dependency > </ dependencies > < configuration > < targetClasses > < param >com.gfg.StringServicesJava.*StringServicesJava*</ param > </ targetClasses > < targetTests > < param >com.gfg.StringServicesJava.*</ param > </ targetTests > </ configuration > </ plugin > </ plugins > </ build > </ project > |
Let’s check the encode and decode method logic here. As informed to a specific scenario it is addressed.
StringEncodeDecodeServicesJava.java
Java
public class StringEncodeDecodeServicesJava { public static String encode(String message) { message = message.toUpperCase(); for ( int i = 0 ; i < message.length(); i++) { if (message.charAt(i) == ',' ) { message = message.replace( ',' , '.' ); } if (message.charAt(i) == '!' ) { message = message.replace( '!' , '.' ); } } return message; } public static String decode(String encodedMessage) { String decodedMessage = encodedMessage.substring( 0 , 1 ).toUpperCase() + encodedMessage.substring( 1 ).toLowerCase(); int count = 0 ; String newMessage = "" ; for ( int i = 0 ; i < decodedMessage.length(); i++) { if (decodedMessage.charAt(i) == '.' ) { newMessage += decodedMessage.substring( 0 , i) + "," ; count = i; break ; } } for ( int i = count + 1 ; i < decodedMessage.length(); i++) { if (decodedMessage.charAt(i) == '.' ) { newMessage += decodedMessage.substring(count + 1 , i) + "!" ; count = i; break ; } } return newMessage; } public static void main(String[] args) { String test = "Hello, this is fun!" ; String encoded = encode(test); //System.out.println("Encoded message : " + encoded); String decoded = decode(encoded); //System.out.println("Decoded message : " + decoded); if (decoded.equals(test)) { System.out.println( "Success! At least for this example!" ); } else { System.out.println( "Something went wrong!" ); } } } |
JUnit testing code with asserts
TestStringEncodeDecodeServicesJava.java
Java
package com.gfg.StringServicesJava; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class TestStringEncodeDecodeServicesJava { @DisplayName ( "Test check for Encode and Decode" ) @Test public void testCheckForEncodeAndDecode() { String testStr = "Hello, this is fun!" ; String encodedStr = StringEncodeDecodeServicesJava.encode(testStr); System.out.println( "Encoded message : " + encodedStr); String decodedStr = StringEncodeDecodeServicesJava.decode(encodedStr); System.out.println( "Decoded message : " + decodedStr); // This yields true when decodedStr.equals(testStr) // and we are checking against true assertEquals( true , decodedStr.equals(testStr)); // We are checking for true assertTrue(decodedStr.equals(testStr)); // We are checking for false by adding !condition assertFalse(!decodedStr.equals(testStr)); boolean [] expectedValues = { decodedStr.equals(testStr), "Hello, this is fun!" .equals(testStr) }; boolean [] actualValues = { true , true }; // Sometimes there are possibilities to // combine more than one condition assertArrayEquals(expectedValues, actualValues); // Sometimes we need to go with assertAll because even // the first assert might have gone wrong but the rest are correct // During that time when we combine everything, even // if there is error it will indicate it assertAll( "Should check whether encoding and decoding are correct" , () -> assertEquals( true , decodedStr.equals(testStr)), () -> assertTrue(decodedStr.equals(testStr)), () -> assertArrayEquals(expectedValues, actualValues)); assertAll( "Should check whether encoding and decoding are correct by checking wrongly" , () -> assertEquals( false , decodedStr.equals(testStr)), () -> assertFalse(decodedStr.equals(testStr)), () -> assertArrayEquals(expectedValues, actualValues)); } } |
Here we need to look carefully for assertAll. When we are writing the code, there are possibilities of producing wrong statements too and because of that if there is a chance of assert to get failed for the first statement, the rest of the statements will not execute at all. We can overcome that scenario by adding assertALL. As a group, it will check all the asserts that are present under assertAll and indicate the errors under one common heading. Here we have kept the heading as “Should check whether encoding and decoding are correct by checking wrongly”. After seeing the output, we can conclude that “assertAll” plays a good role