Java Coding Guidelines and JUnit Rules
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.
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
}
}
-
Log before throwing exception.
RULE-2: Avoid else cases if possible
Avoid else cases if the condition not met either return or throw exception.
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.
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<>();
}
}
-
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.
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.
/* 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;
}
-
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.
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
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
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
@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
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
@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
Post a Comment