1.1 单元测试定义
单元测试(Unit Test,以下简称UT)是编写测试代码,用以检测特定的、明确的、细颗粒度的功能,UT只针对功能点进行测试,一般不包括对业务流程正确性的测试。
1.2 单元测试的意义
(1) UT可以很好的保证代码质量,增加开发人员的信心。
(2) UT可以一定程度提高代码合理性。当我们发现给某一个方法写UT很困难时,比如需要覆盖的分支特别多,那可能说明该方法是可以拆分的;又比如说给某个方法写单测时需要mock的调用十分多,说明该方法违背了单一责任原则,即一个方法只处理一个逻辑做一件事,处理了太多的逻辑,需要进行拆分。
(3) UT能够有效防止回溯问题的出现,即出现之前版本没有而新版本才出现的问题。这种问题的严重程度是最高的,影响也是最恶劣的。因为一般用户可以接受在老版本中本就不存在的功能不可用,但很难接受一个在老版本中用得好好的功能在新版本中突然失效了。在新功能开发后,运行之前的UT,如果发现了未变更逻辑的老功能UT报错,则很有可能是出现了回溯问题。
(4) UT能够帮助开发测试人员确定回归范围。
1.3 单元测试的规范
(1)可衡量,UT 通过率要求100%,行覆盖率要求50%以上。如果有任何一个UT不通过,不是单元测试有问题,就是代码逻辑有问题;覆盖率可以根据具体功能进行挑战,一般不低于40%,通常,越底层的代码覆盖率应该越高,越新的代码覆盖率也应该越高,当代码逻辑变更时,对应的UT 也应该做相应的更改。
(2)独立性:UT之间应该是独立且相互隔离的,一个单元测试只测试一个方法。
(3)对实现类进行测试而非接口,面向接口编程,但面向实现测试
(4)私有方法通过调用类的单测进行测试。私有方法在测试类内没法直接调用,除非使用反射或其他Mock框架(PowerMock, TestableMock等)
(5)在单测中禁止使用try-catch
1.4 单元测试常用注解
@BeforeClass 全局只会执行一次,而且是第一个运行
@Before 在测试方法运行之前运行
@Test 测试方法
@After 在测试方法运行之后允许
@AfterClass 全局只会执行一次,而且是最后一个运行
@Ignore 忽略此方法
1.5 MAVEN 依赖
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.8.47</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency> |
1.6 DEMO
import junit.framework.TestCase; import org.junit.Test;
public class AvGroupCustomerTest extends TestCase { @Test public void test () { AvGroupCustomer avg = new AvGroupCustomer(); avg.setDescription("wde0"); avg.getValidTime(); assertTrue(true); } |
import com.envision.wind.dimension.management.externalrest.domain.Asset; import com.envision.wind.dimension.management.externalrest.service.RedisCacheService; import com.envision.wind.dimension.management.externalrest.service.dao.AssetMapper; import junit.framework.TestCase; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.when;
public class AssetServiceImplTest extends TestCase { @Mock private AssetMapper mapper; @Mock private RedisCacheService redisCacheService; @InjectMocks private AssetServiceImpl impl; @Test public void testAgcMappingServiceImplTest() { MockitoAnnotations.initMocks(this); when(redisCacheService.getValueByRedisKey(anyString())).thenReturn(null); List<Asset> objectList = new ArrayList<>(); when(mapper.selectAll()).thenReturn(objectList); doNothing().when(redisCacheService).saveValueInRedis(anyString(),anyString(),anyInt()); impl.loadAll("customerId"); when(mapper.selectByPrimaryKey(anyString())).thenReturn(new Asset()); impl.loadDomainById("customerId","id"); assertTrue(true); } } |