About Unit Tests

本文为公司组内的一次关于在 Java 项目上的单元测试的分享,面向的是实习生以及工作年限并不长的员工。

What is Unit Tests

UNIT TESTING is a type of software testing where individual units or components of a software are tested. The purpose is to validate that each unit of the software code performs as expected. Unit Testing is done during the development (coding phase) of an application by the developers. Unit Tests isolate a section of code and verify its correctness. A unit may be an individual function, method, procedure, module, or object.

There are levels of testing in current software engineering

  • Unit Testing
  • Integration Testing
  • System Testing
  • Acceptance Testing

And the Unit Testing is the most easy and most cheap way to guarantee the quality of your development.

How to write Unit Tests

Methods

Simple tests

When you test codes without status, like some string handling, Utlis class. You should write test cases to cover all situation. For example

  • if-else statement all conditions
  • for loop once, 0 times or max times
  • test throw all exceptions that you expect
  • test null String, empty string, normal string, invalid string
  • test null integer (or double), 0, positive number, negative number, number exceed your demand

Test for Utils class is the easiest. What you need to do is to cover all the conditions and some invalid cases.

Advanced tests

Simple tests focus on the Utils class, all the input data you can very easily to generate. But when you write some tests for

  • module rely on the database or cache like Redis
  • module rely on file or network

For these cases, you need to mock some data for tests.

1
2
3
when(mockedBookDAL.getAllBooks()).thenReturn(Arrays.asList(book1, book2));
when(mockedBookDAL.getBook("8131721019")).thenReturn(book1);
when(mockedBookDAL.update(any(), any())).thenReturn(1);

You can check these examples. When you mock the functions belong to other module, you can use these mock codes for your module test cases. The most import thing is that you should not mock too much. The third line of the example is not acceptable, you need to update to

1
2
3
when(mockedBookDAL.update(eq(book1), any())).thenReturn(1);
// OR
when(mockedBookDAL.update(eq(book2), eq(1))).thenReturn(1);

Mock all the arguments means that you dose not test the interaction between your module and dependences. You need to test with the excepted argument that your module will pass to the dependence.

And the second import thing is that you should rely on the package that is fully tested. This may be your Utils class or some famous third-party opensource.

Tools

Mockito

  • very easy to use, mock and stub
  • There are some suggestions form the Mockito home page
    • Do not mock types you don’t own
    • Don’t mock value objects
    • Don’t mock everything
    • Show love with your tests!

Lombok

Docker

  • you can use them to setup a database or Redis for tests, so you don’t need to mock the database operations
  • need the pipeline support

Jenkins

  • Will trigger unit tests running at pipeline and export the test reports to the Sonarqube

When to write Unit Tests

First of all, write the unit tests based on your design document. You know the input and output of your module or class, so you cat write the good cases and bad cases of them. At this time, you will get first version of your program like v1.0.

After that you will go to integration tests or system tests, find some bugs or need to add some new features. You should write some unit tests for these changes. At this time, you will get v1.1.

After that you find the performance is bad or some code need to refactor. You need to update some codes but keep the business logic would not be changed. You can rely on the unit tests codes you have finished at v1.1 and v1.0.

Why we need Unit Tests

First of all, we need to verify our module or functions working as expected. Of course, you can verify your code with some demos or setup local environment to check it’s OK or not. But we are working at a team, someone may change your codes and forget to test all the situations. With Jenkins now, we can rely on the pipeline to run our unit tests at each commit. You can check the tests result conveniently.

Secondly, I think the unit tests are some kinds of document of your module or functions. You can find the valid and invalid input for these codes. You will know when it will raise exception.

Conclusion

单元测试除了保证代码的准确性以外,还有一个重要作用就是当你需要改动或者重构代码时,如果已经有单元测试覆盖到相关代码,那么你的改动就不会影响到已有功能。

我不认同 100% 的单元测试覆盖率的就是 100 分的软件质量。单元测试保证质量是自底向上的,但如 API 或者数据库的设计这样更高层级的设计有问题的话,代码写得再好没有用。设计能提高程序的上限,单元测试能提供程序的下限。

另一方面,我认为在测试的时候遇到测试代码很难写的情况,有可能是设计问题,需要去考虑是否需要进行代码上的重构或者设计上的更新。

References

Unit Testing Tutorial: What is, Types, Tools & Test EXAMPLE (guru99.com)

Getting Started with Mocking in Java using Mockito - DZone Java


About Unit Tests
http://yoursite.com/2022/06/14/about-unit-tests/
Author
Shing
Posted on
June 14, 2022
Licensed under