Unit testing
It is important for everyone to guide unit testing, but it is rare to do well in unit testing. In this class, we learned some principles and best practices of unit testing from the teacher.
Unit testing principles
1.1 Macro principles: AIR principles
At a macro level, unit tests as a whole must adhere to AIR principles.
- A: automation
- R: Repeatability
- I: independence
1.2 Practical operation principle: BCDE principle
- B: Border Border value test
- C: Correct the input and get the expected result
- D: Design is combined with Design documents
- E: Error indicates that the program has an Error
1.3 Introduction to common unit testing frameworks
Second, unit test actual combat
2.1 Basic Examples
- @before Execute Before each test case
- @after Executes After each test case
@RunWith(SpringRunner.class)
@SpringBootTest
public class JUnitDemoTest {
private static final Logger logger = LoggerFactory.getLogger(JUnitDemoTest.class);
@BeforeClass
public static void setUpBeforeClass(a) throws Exception {
logger.debug("before class");
}
@AfterClass
public static void setUpAfterClass(a) throws Exception {
logger.debug("after class");
}
@Before
public void setUp(a) {
logger.debug("setup for this test");
}
@After
public void tearDown(a) {
logger.debug("tearDown for this test");
}
@Test
public void testCase1(a) {
logger.debug("test case 1 excute...");
}
@Test
public void testCase2(a) {
logger.debug("test case 1 excute..."); }}Copy the code
The results
Junitdemotest-before class junitdemotest-started JUnitDemoTest in 16.716 seconds (JVM running for 18.076) JUnitDemoTest - setup for this test JUnitDemoTest - test case 1 excute... JUnitDemoTest - tearDown for this test JUnitDemoTest - setup for this test JUnitDemoTest - test case 1 excute... JUnitDemoTest - tearDown for this test JUnitDemoTest - after classCopy the code
2.2 Transaction related tests
You can control a test case and roll back the transaction when the test is done.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application.properties"})
@Transactional
@Rollback
public class TransactionalTest extends AbstractTransactionalJUnit4SpringContextTests {
private static final Logger logger = LoggerFactory.getLogger(TransactionalTest.class);
@BeforeTransaction
public void beforeTranscationalDo(a) {}@AfterTransaction
public void afterTranscationalDo(a) {}// The transaction is automatically rolled back according to class annotations
@Test
public void testOne(a) {}// Override class annotations automatically roll back the transaction, declared not to roll back
@Rollback(false)
public void testTwo(a) {}/**
* As of Spring 3.0,@NotTransactional is deprecated in favor of moving
* the non-transactional test method to a separate (non-transactional)
* test class or to a @BeforeTransaction or @AfterTransaction method. As
* an alternative to annotating an entire class with @Transactional,
* consider annotating individual methods with @Transactional; doing so
* allows a mix of transactional and non-transactional methods in the
* same test class without the need for using @NotTransactional. * /
@NotTransactional
public void testThree(a) {}}Copy the code
The last NotTransactional was deprecated.
It is recommended that if you want to test a non-transactional method, you write the method separately to a non-transactional test class, or write the test statement to the @beforeTransaction or @AfterTransaction methods.
2.3 Test Coverage
2.4 Database code related unit testing practices
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AdminApplication.class)
public class MyBatisPlusTest {
@Resource
private UserDao userMapper;
@Autowired
//private DruidDataSource druidDataSource;
private HikariDataSource dataSource;
/** * test SQL injection, solution: to verify the special character of the parameter */
@Test
public void testLogin(a){
// SQL injection example
List<User> user=userMapper.login("'' or 1 = 1 limit 1 --"."123456");
// Skip the login link
// Verify whether SQL injection skips login. If not, the test succeeds; otherwise, the test fails
// If you need to check for SQL injection vulnerability, you can use mybatis AbstractSqlInjector
QueryWrapper<User> wrapper=new QueryWrapper();
wrapper.eq("user_name"."'' or 1 = 1 limit 1 --");
wrapper.eq("password"."123456");
List<User> user2=userMapper.selectList(wrapper);
// If no data is found
assertThat(user2==null);
}
/** * if the number of connections is exceeded, obtain the number * no connection prompt timeout, you can catch the exception, prepare a bottom-pocket solution */
@Test
public void testSqlConnectionFull(a){
List list=new ArrayList();
try {
// Increase the maximum load of the validation database in sequence
for(int i=0; i<1000; i++){ list.add(dataSource.getConnection()); } List<User> users=userMapper.selectList(null);
assertThat(users==null);
} catch(SQLException throwables) { throwables.printStackTrace(); }}/** * verify that the database connection has been occupying a growing number of connections, avoiding thread deadlocks, etc. */
@Test
public void testConnectionOverTime(a){
List<User> users=null;
while (true){
users=userMapper.selectList(null);
assertThat(users==null); }}/** * Verify the crazy insert data exception rollback ** consider the business scenario, long business scenario, if the execution fails, then the problem of rolling back the transaction in turn will generally use the distributed transaction SEATA to solve this series of problems ** here is only a unit test if the failure of the simple business will be rolled back */
@Transactional(rollbackFor = Exception.class)
@Test
public void testTransactionRollback(a){
while (true){
User user = new User();
user.setUserName("2222");
user.setPassword("123456");
user.setRealName("testTransactionRollback"); userMapper.insert(user); }}/** * verify unique constraint exception, primary key exception, provide a backstop solution ** fail to save data, not because of constraint exception, insert data failed * insert failure, use autoincrement primary key, unique index exception, need to backup data, facilitate later data audit */
@Test
public void testRuntimeException(a){
try{
User user = new User();
user.setUserName("2222");
user.setPassword("123456");
user.setRealName("testRuntimeException");
assertThat(userMapper.insert(user)).isGreaterThan(0);
}catch (Exception e){
User user = new User();
user.setUserName("2222");
user.setPassword("123456");
user.setRealName("testRuntimeException"); userMapper.insert(user); }}/** * Use the default MybaitS-Plus page search, check whether the module pages are correct * verify that the number of pages per page is too large, query direct memory overflow and other situations, need to use the Mybatis - Plus big data enhancement component * increase the maximum amount of a query data */
@Test
public void testPage(a) {
System.out.println("----- baseMapper built-in page ------");
Page<User> page = new Page<>(1.200000000);
IPage<User> userIPage = userMapper.selectPage(page, new QueryWrapper<User>()
.gt("age".6));
assertThat(page).isSameAs(userIPage);
System.out.println("Total number ------>" + userIPage.getTotal());
System.out.println("Current page number ------>" + userIPage.getCurrent());
System.out.println("Current number of pages displayed ------>" + userIPage.getSize());
print(userIPage.getRecords());
System.out.println("----- baseMapper built-in page ------");
}
@Test
public void testSelectOne(a) {
User user = userMapper.selectById(1L);
System.out.println(user);
}
@Test
public void testInsert(a) {
User user = new User();
user.setRealName("testInsert");
user.setUserName("2222");
user.setPassword("123456");
assertThat(userMapper.insert(user)).isGreaterThan(0);
// Successfully get the writable ID directly
assertThat(user.getId()).isNotNull();
}
@Test
public void testDelete(a) {
assertThat(userMapper.deleteById(3L)).isGreaterThan(0);
assertThat(userMapper.delete(new QueryWrapper<User>()
.lambda().eq(User::getUserName, "smile"))).isGreaterThan(0);
}
@Test
public void testUpdate(a) {
User user = userMapper.selectById(2);
assertThat(user.getUserName()).isEqualTo("123");
assertThat(user.getUserName()).isEqualTo("keep");
userMapper.update(
null,
Wrappers.<User>lambdaUpdate().set(User::getPassword, "1231123").eq(User::getId, 2)); assertThat(userMapper.selectById(2).getPassword()).isEqualTo("1231123");
}
@Test
public void testSelect(a) {
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
@Test
public void testSelectCondition(a) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("max(id) as id");
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
private <T> void print(List<T> list) {
if(! CollectionUtils.isEmpty(list)) { list.forEach(System.out::println); }}}Copy the code