On unit testing

What's the use of unit testing

Improve the work efficiency of the whole team.

Quality code is a contribution to the whole team

Improve the programmer's sense of responsibility and code quality.

Through unit testing, ensure that your code is reliable and complete, which is responsible for yourself and the team

Get a regression test tool for the underlying module

Continuously improve the design without destroying the existing functions

It forces you to plan well before writing code, leading to better design, including coding level.

Reduce the cost of Bug repair

Noun interpretation

Unit test: it is the test of the basic components of software. It belongs to the category of white box test. It designs test cases by analyzing the logical structure of code.

Regression test: retest the previous test after the modification to ensure the correctness of the modification.

Smoke test: developers spend less time to ensure that the function can run at least.

Stub module: simulate the "double" module of the third party, parameters and return information.

Drive module: simulate user operation and generated data, enable the tested module to transmit data to the tested module, and make expected judgment on the returned information results.

Non incremental testing: test each module separately, and finally connect all modules together for testing.

Incremental testing: test the modules to be tested and the modules that have been tested together.

Unit test requirements

Test requirements document.

Unit tests are written by the person who is most familiar with the code (the writer of the program).

Ensure the independence of unit testing, that is, test cases cannot depend on each other.

The unit test shall ensure that the test data generated will not affect the system. It is generally recommended to roll back the data generated in the test to avoid affecting the normal data of the system.

Test granularity. Generally, all methods are required to be tested (excluding get and set methods).

Assertions must be used in the test to determine whether the results meet the expected goals.

It is necessary to record the test results and have the result documents. According to the different test requirements, record selectively, analyze the results and debug them.

When the requirements change, regression testing is required to ensure that no new bug s are generated.

When involved with the third party, it is necessary to write the pile module of the third party and simulate the parameters, logic, data and return value to achieve the same effect.

I suggest that after the method parameters and names are defined, write test cases first, and then realize the functional logic.

Specification

Test class name: the column name of the class to be tested + test. For example - > the name of the class to be tested is BaseBizTest, then the name of the test class is BaseBizTest

Test method name: test + name of the tested method. For example, if the name of the tested method is getUser, the name of the test method is testGetUser

Test package name: the package name of the tested class is com yunat. Channel, the package name of the test class is test com. yunat. The project managed by channel and Maven establishes the same package path under src/test/java.

Select test framework

Choose according to different requirements. If you are testing the database, you can choose dbunit and junit. For business framework testing, you can choose spring test and junit testing framework. The following are pom dependencies:

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.10</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>3.1.1.RELEASE</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>org.dbunit</groupId>
	<artifactId>dbunit</artifactId>
	<version>2.4.7</version>
	<scope>test</scope>
</dependency>

Introduce junit4 annotation commonly used in 10 frameworks

@BeforeClass: the method marked by it must be a static method, which is executed when the test class object is loaded.

@AfterClass: the method marked by it must be a static method, which is executed after all methods of the test class are executed.

@Before: execute before each test method.

@After: execute after each test method.

@Ignore: indicates that the test method is ignored.

@Test: the test method is marked as drive.

@RunWith: test runner, collection runner, Suite, parameterized runner, etc.

@Suitecalasses: the type parameter of the test runner. Put your own test class and run it comprehensively, that is, set test.

For more detailed introduction, please search: junit_ Use guide and operation specification (compare junit 3 and 4) pdf.

Parameterized parameterized runner usage

Parameterized demo:

Tested class:

public class WordDealUtil {
    public static Object wordFormat4DB(String target) {
        if (target==null) return null;
        if ("employeeInfo".equals(target)) return "employee_info";
        if ("".equals(target)) return "";
        if ("EmployeeInfo".equals(target)) return "employee_info";
        if ("employeeInfoA".equals(target)) return "employee_info_a";
        if ("employeeAInfo".equals(target)) return "employee_a_info";
        return null;
    }
}

Test case:

/**
 * Produce different results for different parameters
 *
 * @author ming.peng
 * @date 2012-12-27
 */
@RunWith(Parameterized.class)
public class WordDealUtilTest {
	private String expected;
	private String target;
	@Parameters
	public static Collection<Object[]> words() {
		return Arrays.asList(new Object[][] {
				{ "employee_info", "employeeInfo" }, // Test general handling
				{ null, null }, // Processing when testing null
				{ "", "" }, // Processing when testing an empty string
				{ "employee_info", "EmployeeInfo" }, // Test when the first letter is capitalized
				{ "employee_info_a", "employeeInfoA" }, // Test when the last letter is capitalized
				{ "employee_a_info", "employeeAInfo" } // Test the case when multiple connected letters are capitalized
				});
	}
	/**
	 * Constructor required for parameterized tests
	 *
	 * @param expected
	 *            The expected test result corresponds to the first parameter in the parameter set
	 * @param target
	 *            Test data, corresponding to the second parameter in the parameter set
	 */
	public WordDealUtilTest(String expected, String target) {
		this.expected = expected;
		this.target = target;
	}
	/**
	 * Test the conversion of Java object name to database name
	 */
	@Test
	public void wordFormat4DB() {
		assertEquals(expected, WordDealUtil.wordFormat4DB(target));
	}
}

8. Use of spring test framework

Because most of our system frameworks use spring framework, and junit is not very convenient to test the framework, we choose spring test and junit test framework to be used together. Spring test is an expansion based on junit framework.

classpath can be used in two ways:

  1. classpath*:applicationContext.xml will be added to the applicationContext.xml in the root directory of this project and all jar packages XML file, used when cross jar package dependency.

  2. classpath:applicationContext.xml is only added to the file in the root directory of the project
    applicationContext.xml file. It is recommended to write this when it does not depend on the configuration files of other jar packages to avoid conflicts.

The commonly used annotation

@ContextConfiguration: configure the reading resources of spring

@Transactional: transaction declaration

@TransactionConfiguration: transaction configuration

@Rollback: transaction rollback configuration

spring business component test example

package com.yunat.devtwo.biz;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.yunat.devtwo.dao.BaseDao;
/**
 * Business test component
 *
 * @author ming.peng
 * @date 2012-12-26
 * @since 2.2.0
 */
@Component("BaseBiz")
public class BaseBiz {
	@Autowired
	private BaseDao baseDao;
	public Map<String, Object> getUser(String userName){
		Map<String, Object> parameter = new HashMap<String, Object>(1);
		parameter.put("userName", userName);
		Map<String, Object> user = baseDao.getSingleRow("Common.getUser", parameter);
		return user;
	}
	public Integer saveSysConfig(){
		Map<String, Object> sysConfig = new HashMap<String, Object>();
		sysConfig.put("name", "gateway_balance");
		sysConfig.put("desc", "Channel account initialization amount");
		sysConfig.put("value", "10000");
		return baseDao.save("Common.saveSysConfig", sysConfig);
	}
}

For the above business components, it is very simple to use annotation to configure test cases, as follows:

package com.yunat.devtow.biz;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.yunat.devtwo.biz.BaseBiz;
/**
 * Business component testing
 *
 *
 * @author ming.peng
 * @date 2012-12-27
 * @since 2.2.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/applicationContext.xml" })
@Transactional
@TransactionConfiguration(defaultRollback=false, transactionManager="txManager")
public class BaseBizTest {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private BaseBiz baseBiz;
	@Test
	public void testGetUser(){
		Map<String, Object> user = baseBiz.getUser("qiushi");
		logger.info(user.toString());
		Assert.assertEquals(77800, user.get("group_id"));
	}
	@Test
	public void testSaveSysConfig(){
		Integer result = baseBiz.saveSysConfig();
		Assert.assertEquals(result.intValue(), 1);
	}
}

Spring test provides a very simple test for the web layer. The following test cases for spring MVC:

package com.yunat.devtwo.controller;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.yunat.devtwo.biz.BaseBiz;

/**
 *
 * spring mvc controller
 *
 * @author ming.peng
 * @date 2012-12-27
 * @since 2.2.0
 */
@Controller
@RequestMapping("ccmsController")
public class CcmsController {
	private Logger logger = Logger.getLogger(this.getClass());
	@Autowired
	private BaseBiz baseBiz;
	/**
	 * Enter the account management interface
	 * @param userName
	 * @param userInfo
	 * @return
	 */
	@RequestMapping("accountManager")
	public ModelAndView accountManager(String userName, String userInfo, HttpSession session) {
		logger.info("===================>>>>get into ccms Account management center, user name:"+userName);
		ModelAndView mav = new ModelAndView("ccms/account");
		mav.addObject("userName", userName);
		mav.addObject("userInfo", userInfo);
		System.out.println(session.getAttribute("abc"));
		Map<String, Object> user = baseBiz.getUser(userName);
		logger.info("user:" + user);
		baseBiz.saveSysConfig();
		logger.info("===================>>>>Normal entry ccms Account management center, user name:"+userName);
		session.setAttribute("userName", userName);
		return mav;
	}
}

It is also very simple to configure and test web layer components using annotation, as follows:

package com.yunat.devtow.controller;
import javax.servlet.http.HttpSession;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
 * CcmsControlle test
 *
 *
 * @author ming.peng
 * @date 2012-12-27
 * @since 2.2.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/applicationContext.xml", "classpath:spring/springmvc-servlet.xml" })
@Transactional
@TransactionConfiguration(defaultRollback = false, transactionManager = "txManager")
public class CcmsControllerTest {
	@Autowired
	private RequestMappingHandlerAdapter handlerAdapter;
	@Autowired
	private RequestMappingHandlerMapping handlerMapping;
	private static MockHttpServletRequest request;
	private static MockHttpServletResponse response;
	@BeforeClass
	public static void before() {
		request = new MockHttpServletRequest();
		request.setCharacterEncoding("UTF-8");
		response = new MockHttpServletResponse();
	}
	@Test
	@Rollback(true)
	public void testAccountManager() {
		request.setRequestURI("/ccmsController/accountManager");
		request.setMethod(HttpMethod.POST.name());
		request.setParameter("userName", "qiushi");
		request.setParameter("userInfo", "abjiozjdlfjaosdifjoaisdfji");
		HttpSession session = request.getSession();
		session.setAttribute("abc", "dbc");
		ModelAndView mv = null;
		try {
			Object handler = handlerMapping.getHandler(request).getHandler();
			mv = handlerAdapter.handle(request, response, handler);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Assert.assertNotNull(mv);
		Assert.assertEquals(response.getStatus(), 200);
		Assert.assertEquals(mv.getViewName(), "ccms/account");
	}
}

The choice of framework here is mainly for some frameworks commonly used in our system. Choosing a special testing framework has simplified our daily testing work and improved work efficiency.

last

I hope that the article will help you. If you have other questions, please welcome to come to exchange and supplement (pay attention to WeChat official account: program yuan Muzi to collect massive software testing resources (clear thinking, sometimes more important than the exact answers), share more technology and interview information, you can join qq(644956177), and there are also peers to exchange technology together.

Keywords: Java Programming unit testing software testing

Added by techker on Tue, 08 Mar 2022 07:07:07 +0200