Java Coding Guidelines and JUnit Rules

Coding Guidelines

Coding Guidelines

Following coding guide lines are very simple rules but it has big impact on code quality, readability, indentation and test coverage.

  • Improves readability of the code with proper indentation

  • Helps to write Test compatible code (You can write better positive/negative tests)

  • Helps to write more optimized code

Note
java doesn’t care about indentation but good developers does.

RULE-1 : Log Before throwing Exception

When precondition not matches , we usually throw exception from the method. it is recommended to log before throwing exception.

StudentService.java
public class StudentService{

    /* NOT RECOMMENDED */
    public Student getStudentByFirstName(String firstName) throws StudentNotFoundException{

        if(firstName==null){
            throw new StudentNotFoundException("Invalid firstName provided");
        }

        // write positive scenario
    }

    /* RECOMMENDED */
    public Student getStudentByFirstName(String firstName) throws StudentNotFoundException{

        if(firstName==null){
            LOG.error("Invalid firstName provided"); //(1)
            throw new StudentNotFoundException("Invalid firstName provided");
        }

        // write positive scenario
    }
}
  1. Log before throwing exception.

RULE-2: Avoid else cases if possible

Avoid else cases if the condition not met either return or throw exception.

StudentService.java
public class StudentService{

     /* NOT RECOMMENDED */
    public Student getStudentByFirstName(String firstName) throws StudentNotFoundException{

        if(firstName==null){
            LOG.error("Invalid firstName provided");
            throw new StudentNotFoundException("Invalid firstName provided");
        }else{
             // AVOID THIS ELSE BLOCK , as we already throwing exception.
        }

    }

     /* RECOMMENDED */
    public Student getStudentByFirstName(String firstName) throws StudentNotFoundException{

        if(firstName==null){
            LOG.error("Invalid firstName provided");
            throw new StudentNotFoundException("Invalid firstName provided");
        }

        // WRIT YOUR LOGIC DIRECTLY HERE WITH VALID firstName,

    }

}

RULE-3: Don’t write big conditional blocks

Return results immediately instead of assigning to local variables.

StudentService.java
public class StudentService{

      /* NOT RECOMMENDED */
    public List<Student> getStudentByAge(int ageLimit){

        List<Student> list=null; // avoid this reference variables

        if (ageLimit<=10){
            //prepare list
            list=new ArrayList<>();
        }else{
            //
            // don't
            // write
            // big
            // code
            // block
            // inside else block
            list=new ArrayList<>();
        }

        return list;
    }

      /* RECOMMENDED */
    public List<Student> getStudentByAge(int ageLimit){
        if (ageLimit<=10){
            //prepare list
            return new ArrayList<>(); //(1)
        }


      //WRITE BIG LOGIC BLOCK WITHOUT ELSE
      //SO THAT INDENTATION will be 1st level
       return new ArrayList<>();
    }
}
  1. Return immediately when results prepared for a specific condition.

RULE-4: Don’t return null Collection Type from method

Never return null collection type (to support java streams), and never return null object type.

StudentService.java
import java.util.Collections;
public class StudentService{

       /*NOT  RECOMMENDED */
    public List<Student> getStudents(int age){

        if(age <=10){
            return null;
        }

        //other logic
    }

    /* RECOMMENDED */
    public List<Student> getStudents(int age){

        if(age <=10){
            return Collections.emptyList();
        }

        //other logic
    }

}

RULE-4: Define constants in Utility class, not in interface

Utility class must have Private Constructor and final, so that it can’t be extended. all the constants in Utility must be imported as static.

Utility.java
  /* NOT RECOMMENDED */
public interface DemoInterface{
     int AGE=9;
}

  /* RECOMMENDED */
public final class DemoUtility{
    private DemoUtility(){
        //utility class  must have private construction and final (1)
    }
    public static final int AGE=9;

}
  1. Utility / Factory class must have private construction and final, so that it show 100 codeCoverage.

RULE-5: Always Use Runtime exceptions

If your method throwing exception, if certain condition not met, make sure your custom exception should extend from RuntimeException, not Exception. This avoid putting more try-catches at client side and makes your APIs java8 lambda compatible.

ServiceException.java
public class ServiceException extends RuntimeException{
    //override default constructors
}

JUnit Testing Rules

UnitTest with ExpectedException Rule

If a method throwing exception use Junit @Rule if you are using Junit < 4.13

StudentServiceTest.java
public class StudentServiceTest {

    @Rule
    public final ExpectedException thrown = ExpectedException.none();

    @Test
    public void getStudentsTest() {
        //expect:
        thrown.expect(StudentNotFoundException.class);
        thrown.expectMessage("Student not found");

        //given:
        StudentService studentService=new StudentServiceImpl();

        //when:
        studentService.getStudents(29);
    }
}

UnitTest with assertThrows (Java8 Lambda)

If you are using Java8 Use below syntax

StudentServiceTest.java
public class StudentServiceTest {

  @Test
  public void getStudentsTest() {
    //given:
    final StudentService studentService = new StudentServiceImpl();

    //when:
    ThrowingRunnable methodUnderTest=()-> studentService.getStudents(12);
    Throwable exception = assertThrows(StudentNotFoundException.class,methodUnderTest);

    //then:
    assertEquals("Student not found", exception.getMessage());

  }
}

UnitTest with System.exit() Rule

If you are building CLI Tool with different use-cases, you can use ExpectedSystemExit Rule to test that functionality

MyCLIToolTest.java
@RunWith(PowerMockRunner.class) //(1)
@PrepareForTest(MyCLITool.class) //(2)
@PowerMockIgnore("javax.management.*")
public class MyCLIToolTest {

  /**
   * ExpectedSystemExit Rule to verify all test methods exited with status 0
   */
  @Rule
  public final ExpectedSystemExit exit = ExpectedSystemExit.none();


  @Mock
  StudentService studentService;

  @Before
  public void setup() {
    PowerMockito.mockStatic(MyCLITool.class); //(3)
    Mockito.when(MyCLITool.getStudentService()).thenReturn(studentService);
  }

  @Test
  public void helpOptionTest() {

    //expect: exit code
    exit.expectSystemExitWithStatus(0); //(4)

    //when:
    MyCLITool.main(new String[]{"-h"});

  }
}

UnitTest with Timeout Rule

If you want to service API execution time with in the given ETA , use Timeout Rule

TimeoutRuleTest.java
public class TimeoutRuleTest {

  @Rule
  public Timeout timeout = Timeout.seconds(5);
  // Test fails if your unitTest taking more than 5 seconds.

  @Test
  public void timeoutTest() throws InterruptedException {
    TimeUnit.SECONDS.sleep(6);
  }
}

UnitTest with TemporaryFolder Rule

TemporaryFolderRuleTest.java
@RunWith(BlockJUnit4ClassRunner.class)
public class TemporaryFolderRuleTest {

    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Test
    public void fileTest() throws Exception{
        File file = temporaryFolder.newFile("CoderGists.txt");
        assertThat(file.exists(), is(true));
    }
}

Plugins

If you are IntelliJ IDEA , please use below plugins which helps you to write better code

Save action plugin

This plugin will helps you to "optimize imports", "reformat code", "rearrange code with proper indentation", adds this , private, static wherever it missed.

SonarLint plugin

Static code analyzer pre loaded with sufficient rules

Comments

Popular posts from this blog

IBM Datapower GatewayScript

Spring boot Kafka Integration

Spring boot SOAP Web Service Performance