广州公司宣传片设计,北京做seo的公司,一个网站的建设需要什么,怎么黑入网站如何正确写单元测试 单元测试重要性写单元测试时存在的问题1、如何命名测试类方法1.1、测试类命名规范1.2、测试方法命名规范 2、测试类的要求2.1测试行覆盖率100%2.2、单一职责2.3、可重复2.4、外部隔离#xff0c;无任何外部依赖2.5、正确的使用断言2.6、不应该为了测试… 如何正确写单元测试 单元测试重要性写单元测试时存在的问题1、如何命名测试类方法1.1、测试类命名规范1.2、测试方法命名规范 2、测试类的要求2.1测试行覆盖率100%2.2、单一职责2.3、可重复2.4、外部隔离无任何外部依赖2.5、正确的使用断言2.6、不应该为了测试方便修改线上代码2.7、线上bug应该沉淀为测试用例2.8、快速原则 3、选择测试框架3.1、基础测试框架3.2、如何在外部隔离的前提下测试DAO层 4、如何获得测试覆盖率4.1、使用idea工具获取测试覆盖率情况 5、关于 PowerMockito 工具的简单 demo5.1、使用powermockito,在test类名上使用5.2、普通对象的mock5.3、静态方法的mock5.4、静态方法的void方法mock模拟抛出异常5.5、模拟测试构造函数5.6、如何测试是否打印了日志5.7、测试预期抛出异常5.8、注入依赖 单元测试重要性
微软公司之前有这样一个统计bug在单元测试阶段被发现的平均耗时是3.25小时如果遗漏到系统测试则需要11.5个小时。由此可见单元测试的重要性。
写单元测试时存在的问题
虽然单元测试很重要但是在工作中还是会发现不少同学在书写单元测试时存在许多问题我将常见的问题总结如下
依赖了SpringBootTest框架由于过长的启动耗时导致代码单测的代价很大这就极大限制了把单测作为一个日常态运行。不可重复测试常见于写数据接口往往写入后由于数据的唯一性检验导致测试用例在每次测试前都需要该。覆盖度不够往往只测试自己想验证的代码分支而忽略了其他重要的的代码逻辑。无从下手写有些类有着比较复杂的静态和环境的依赖无从测起。缺乏断言执行完测试用例无法明确是否验证了逻辑
下面我写一个单元测试的demo并介绍常用的powermock框架。
一个测试用例的demo:
RunWith(PowerMockRunner.class)
PrepareForTest(PrivatePartialMockingExample.class)
public class PrivatePartialMockingExampleTest{Testpublic void demoPrivateMethodMocking() throws Exception {final String expected TEST VALUE;final String nameOfMethodToMock methodToMock;final String input input;PrivatePartialMockingExample underTest PowerMockito.spy(new PrivatePartialMockingExample());PowerMockito.when(underTest, nameOfMethodToMock, input).thenReturn(expected);String actual underTest.methodToTest();assertEquals(expected, actual);verifyPrivate(underTest).invoke(nameOfMethodToMock, input);}
}1、如何命名测试类方法
1.1、测试类命名规范
测试类必须和被测试类在同一个包内测试类名字必须由被测试类类目拼接“Test”构成比如com.test.Dummy.class它的测试类是com.test.DummyTest.class
1.2、测试方法命名规范
当一个方法的使用场景比较复杂为遵循单一职责原则应考虑一个方法对应多个测试用例方法这个时候测试方法的命名需要和被测试方法不同。
被测方法期待行为触发条件 例子 boolean isChild() --------isChildFalseAgeBiggerThan18() int getMoney()----------getMoneyThrowExceptionlfUserNotExist()
2、测试类的要求
2.1测试行覆盖率100%
这个是最重要的要求既然写了测试类它的行覆盖率要求就是 100%没有达到没有写测试。
另外应该明确认知到行覆盖率不等于测试覆盖率。根据经验刚刚好做到 100% 行覆盖基本上整体的测试覆盖率在 10%~30%还是远远不够的提升质量还需要在行覆盖率的基础上尽量做到更多的测试覆盖。
2.2、单一职责
每个测试方法只针对一个方法测试不要测试多个方法。多个测试方法之间不要有任何依赖比如测试查询用户的方法依赖了插入用户的方法。每个测试方法的长度应做控制不建议超过 50 行。不同的场景应尽量分拆测试方法。
2.3、可重复
在被测试方法未变化的情况下测试用例要做到可以重复无限次调用。
Badcase:
Test
public void dummy(){ Assert.assertTrue(System.currentTimeMillis() %20);
}2.4、外部隔离无任何外部依赖
单测中应不与任何外部环境交互不应有任何的 IO 交互这样才能保证测试用例的成功率。
如 redis 或者 db 或者外部系统 RPC 的依赖应尽量使用本地的 db 或者 mock 的 bean 模拟避免跑测试用例的时候因为外部系统不稳定或者网络不通无法测试。数据库可以使用 in memory db 模拟java 可以使用 h2database。
2.5、正确的使用断言
没有断言等于没有写测试。每个测试方法必须有至少一个断言语句。即使没有返回的方法可以来校验日志打印是否被正确打印了或者方法是否没有出异常。应多使用非 expect true 的方式这样便于查看出错信息。
2.6、不应该为了测试方便修改线上代码
Badcase:
//...something upper
if(!TestContext.isUnitTest()){
}
//...something down
DummyService.doRealThing();2.7、线上bug应该沉淀为测试用例
每个 bug 修复上线后应有避免出现类似问题的单元测试确保下次不会出同样问题。
2.8、快速原则
每个单元测试类的耗时不应大于 100ms为了能快速对整个项目测试应控制测试类的依赖、复杂度提升运行速度这样才能做到测试常态化。
3、选择测试框架
3.1、基础测试框架
单元测试框架最常用的是 JUnit4/5。但在现实场景中JUnit 往往力不从心因为要满足外部隔离和快速测试的要求。对于代码中的静态依赖、final、spring bean 依赖等情况在不真实启动容器或者面对巨大静态依赖的前提下快速地将待测试的逻辑充分测试这就要用到一个比较好用的工具PowerMock
PowerMock 扩展自 Mockito通过 Java 反射机制解决了 Mockito 的一些问题并通过提供定制的类加载器以及一些字节码改写技巧的应用。PowerMock 在 Mockito 基础上实现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持对静态初始化过程的移除等强大功能是实现外部隔离和快速原则的利器。
3.2、如何在外部隔离的前提下测试DAO层
由于 mybatis 集成度较高需基于 SpringBootTest 前提下测试但真实数据库可用 H2 Database 做替代避免远程 IO 交互不违背外部隔离原则。
新建test-application.properties文件配置 H2 Database 的基本信息和 MyBatis 的 mapper location
# mysql 驱动:h2
spring.datasource.driver-class-nameorg.h2.Driver
# h2 内存数据库库名:test
spring.datasource.urljdbc:h2:mem:test
#初始化数据表
spring.datasource.schemaclasspath:init_table.sql
spring.datasource.username
spring.datasource.password
# 打印 SQL 语句, Mapper 所处的包
logging.level.com.hawkingfoo.daodebug
#放 mapper 的地方
mybatis.mapper-locationsclasspath:/sqlmaps/*.xml新建init_table.sql文件文件名与test-application.properties文件中的spring.datasource.schema行的值一致。注意创建表语句中应去除最后一行ENGINEXXX否则 H2 Database 执行时会报符号错误。例如创建student表的语句
DROP TABLE IF EXISTS student;
CREATE TABLEstudent( )ENGINEInoDB DEFAULT CHARSETutf8mb4; PRIMARY KEY(id) id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(1024) NOT NULL, sextinyint(1) NOT NULL, addr varchar(1024) NOT NULL,在测试目录下增加 Spring Boot 的启动类DaoTestSpringBootAppication注意配置scanBasePackages只扫描 DAO 相关类避免初始化无关的类。
SpringBootApplication(scanBasePackagescom.onx.buyerhome.service.infra.db)
PropertySource(classpath:test-application.properties)
public class DaoTestSpringBootApplication{public static void main(String[] args){SpringApplication.run(DaoTestSpringBootApplication.class, args);}
}新建Base Test类用于test类继承使用
RunWith(SpringRunner.class)
public class DaoTestBase {
}最后可以愉快的写Dao的测试用例了
public class BuyerPlanMapperTest extends DaoTestBase {ResourceBuyerPlanMapper buyerPlanMapper;Testpublic void queryByIds(){ListPlano plansbuyerPlanMapper.querybyIds(Lists.newArrayList(101L));Assert.assertTrue(plans.size()0);Assert.assertEquals(Long.valueOf(101L), plans.get(0).getId());}
}4、如何获得测试覆盖率
4.1、使用idea工具获取测试覆盖率情况
可以直接使用idea的覆盖率工具来查看测试用例的覆盖情况
在测试类名上弹开右键菜单选择使用覆盖率运行。查看单个类的总体覆盖情况。查看类里面具体行的覆盖情况左边显示为绿色的即为覆盖的行为红的即为没有覆盖到。
5、关于 PowerMockito 工具的简单 demo
列举了一些常见情况下 PowerMockito 的用法实际使用中若有其他疑惑可自行搜索工具检索。
5.1、使用powermockito,在test类名上使用
RunWith(PowerMockRunner.class)
RunWith(PowerMockRunner.class)
public class RpcClientAopLogTest{
}5.2、普通对象的mock
public class RpcClientAopLogTest{CommonsConfigHolder commonsConfigHolder;Beforepublic void setUp(){commonsConfigHolder PowerMockito.mock(CommonsConfigHolder.class);PowerMockito.when(commonsConfigHolder.getCommonsConfig()).thenReturn(new Comm());}
}5.3、静态方法的mock public void setUp(){PowerMockito.mockStatic(ProfilerUtil.class);PowerMockito.when(ProfilerUtil.getCurrentUid()).thenReturn(1L);}5.4、静态方法的void方法mock模拟抛出异常 public void setUp(){PowerMockito.mockStatic(ProfilerUtil.class);doThrow(new RuntimeException()).when(ProfilerUtil.class);ProfilerUtil.start(exception test);}5.5、模拟测试构造函数
public class User{private String username;private String password;public User(String username, String password) {this.username username;this.password password;}public void insert(){throw new UnsupportedOperationException();}
}
public class UserService {public void saveUser(String username, String password) {User user new User(username, password);user.insert();}
}RunWith(PowerMockRunner.class)
PrepareForTest(UserService.class)
public class UserServiceTest {Mockprivate User user;Testpublic void saveUser() throws Exception {String username user1;String password aaa;// 在构造函数被调用的使用返回了我们构造的类PowerMockito.whenNew(User.class).withArguments(username, password).thenReturn(user);PowerMockito.doNothing().when(user).insert();UserService userService new UserService();userService.saveUser(username, password);Mockito.verify(user).insert();}
}5.6、如何测试是否打印了日志
public class Dummy{private static LogCaptor logCaptor;private static final String EXPECTED_INFO_MESSAGE Keyboard not responding. Pre;BeforeAllpublic static void setupLogCaptor() {logCaptor LogCaptor.forClass(FooService.class);}AfterEachpublic void clearLogs() {logCaptor.clearLogs();}AfterAllpublic static void tearDown() {logCaptor.close();}Testpublic void logMethod() {// do something that triggers logging in FooService// Assuming there is a method in FooService that logs the expected message.assertThat(logCaptor.getInfoLogs()).containsExactly(EXPECTED_INFO_MESSAGE);}
}5.7、测试预期抛出异常
Test(expected IllegalStateException.class)
public void dummy() {// do somethingthrow new IllegalStateException();
}5.8、注入依赖 /*** 这个测试类展示了如何使用 PowerMock 和 Mockito 进行测试。* RunWith(PowerMockRunner.class) 告诉 JUnit 使用 PowerMockRunner 进行测试。* PrepareForTest({MockUtil.class}) 表示要为指定的类准备测试环境这里是 MockUtil 类* 适用于模拟 final 类或有 final、private、static、native 方法的类。*/
RunWith(PowerMockRunner.class)
PrepareForTest({MockUtil.class})
public class MockExample{/*** InjectMocks 注解用于将被测试类的依赖自动注入到该实例中。* 这里会将模拟的依赖注入到 MockServiceImpl 实例中。*/InjectMocksprivate MockServiceImpl mockService;/*** Mock 注解用于创建模拟对象。这里创建了一个 MockMapper 的模拟对象。*/Mockprivate MockMapper mockMapper;/*** 测试方法用于测试某个特定的功能。* 在这个方法中首先创建了一个 MockModel 对象然后使用 PowerMockito 模拟了 mockMapper 的 count 方法的返回值为 2。* 最后使用 assertEquals 断言来验证 mockService 的 count 方法的返回值是否与预期一致。*/Testpublic void testSomething(){MockModel model new MockModel();PowerMockito.when(mockMapper.count(model)).thenReturn(2);assertEquals(2, mockService.count(model));}
}
文章转载自: http://www.morning.hxsdh.cn.gov.cn.hxsdh.cn http://www.morning.ddrdt.cn.gov.cn.ddrdt.cn http://www.morning.qmbgb.cn.gov.cn.qmbgb.cn http://www.morning.rsdm.cn.gov.cn.rsdm.cn http://www.morning.ghjln.cn.gov.cn.ghjln.cn http://www.morning.cltrx.cn.gov.cn.cltrx.cn http://www.morning.ldzxf.cn.gov.cn.ldzxf.cn http://www.morning.gwdmj.cn.gov.cn.gwdmj.cn http://www.morning.zbhfs.cn.gov.cn.zbhfs.cn http://www.morning.grpbt.cn.gov.cn.grpbt.cn http://www.morning.rgtp.cn.gov.cn.rgtp.cn http://www.morning.qbjrf.cn.gov.cn.qbjrf.cn http://www.morning.jwlmm.cn.gov.cn.jwlmm.cn http://www.morning.xkmrr.cn.gov.cn.xkmrr.cn http://www.morning.lgnz.cn.gov.cn.lgnz.cn http://www.morning.qyfrd.cn.gov.cn.qyfrd.cn http://www.morning.wnkqt.cn.gov.cn.wnkqt.cn http://www.morning.nslwj.cn.gov.cn.nslwj.cn http://www.morning.xlbyx.cn.gov.cn.xlbyx.cn http://www.morning.tklqs.cn.gov.cn.tklqs.cn http://www.morning.tbrnl.cn.gov.cn.tbrnl.cn http://www.morning.wyfpc.cn.gov.cn.wyfpc.cn http://www.morning.zqwp.cn.gov.cn.zqwp.cn http://www.morning.swsrb.cn.gov.cn.swsrb.cn http://www.morning.xinxianzhi005.com.gov.cn.xinxianzhi005.com http://www.morning.pabxcp.com.gov.cn.pabxcp.com http://www.morning.sfhjx.cn.gov.cn.sfhjx.cn http://www.morning.jpgfq.cn.gov.cn.jpgfq.cn http://www.morning.syynx.cn.gov.cn.syynx.cn http://www.morning.qrmry.cn.gov.cn.qrmry.cn http://www.morning.dzgyr.cn.gov.cn.dzgyr.cn http://www.morning.pakistantractors.com.gov.cn.pakistantractors.com http://www.morning.fdrch.cn.gov.cn.fdrch.cn http://www.morning.frtb.cn.gov.cn.frtb.cn http://www.morning.grpfj.cn.gov.cn.grpfj.cn http://www.morning.mymz.cn.gov.cn.mymz.cn http://www.morning.tqpnf.cn.gov.cn.tqpnf.cn http://www.morning.jrksk.cn.gov.cn.jrksk.cn http://www.morning.nzdks.cn.gov.cn.nzdks.cn http://www.morning.dtrcl.cn.gov.cn.dtrcl.cn http://www.morning.wbxr.cn.gov.cn.wbxr.cn http://www.morning.pctsq.cn.gov.cn.pctsq.cn http://www.morning.gtqws.cn.gov.cn.gtqws.cn http://www.morning.kcwkt.cn.gov.cn.kcwkt.cn http://www.morning.rfxw.cn.gov.cn.rfxw.cn http://www.morning.jwefry.cn.gov.cn.jwefry.cn http://www.morning.ckhry.cn.gov.cn.ckhry.cn http://www.morning.jghty.cn.gov.cn.jghty.cn http://www.morning.fdmtr.cn.gov.cn.fdmtr.cn http://www.morning.wjqyt.cn.gov.cn.wjqyt.cn http://www.morning.dwhnb.cn.gov.cn.dwhnb.cn http://www.morning.tgmwy.cn.gov.cn.tgmwy.cn http://www.morning.lngyd.cn.gov.cn.lngyd.cn http://www.morning.zrfwz.cn.gov.cn.zrfwz.cn http://www.morning.mmhyx.cn.gov.cn.mmhyx.cn http://www.morning.yrhpg.cn.gov.cn.yrhpg.cn http://www.morning.gxqpm.cn.gov.cn.gxqpm.cn http://www.morning.bykqg.cn.gov.cn.bykqg.cn http://www.morning.tjwlp.cn.gov.cn.tjwlp.cn http://www.morning.mmosan.com.gov.cn.mmosan.com http://www.morning.wbxr.cn.gov.cn.wbxr.cn http://www.morning.fznj.cn.gov.cn.fznj.cn http://www.morning.tclqf.cn.gov.cn.tclqf.cn http://www.morning.nlmm.cn.gov.cn.nlmm.cn http://www.morning.zcwwb.cn.gov.cn.zcwwb.cn http://www.morning.snxbf.cn.gov.cn.snxbf.cn http://www.morning.hctgn.cn.gov.cn.hctgn.cn http://www.morning.rpjyl.cn.gov.cn.rpjyl.cn http://www.morning.xmjzn.cn.gov.cn.xmjzn.cn http://www.morning.rckmz.cn.gov.cn.rckmz.cn http://www.morning.yhywx.cn.gov.cn.yhywx.cn http://www.morning.qkwxp.cn.gov.cn.qkwxp.cn http://www.morning.mdlqf.cn.gov.cn.mdlqf.cn http://www.morning.wtdyq.cn.gov.cn.wtdyq.cn http://www.morning.hbkkc.cn.gov.cn.hbkkc.cn http://www.morning.lyjwb.cn.gov.cn.lyjwb.cn http://www.morning.fxzlg.cn.gov.cn.fxzlg.cn http://www.morning.tdfyj.cn.gov.cn.tdfyj.cn http://www.morning.zfcfk.cn.gov.cn.zfcfk.cn http://www.morning.fdfdz.cn.gov.cn.fdfdz.cn