不点
,我们哪来故事?
一个指导程序员进入大公司/独角兽 的精品社群,致力于分享职场达人的专业打法,包括「学习路线+简历模板+实习避坑+笔试面试+试用转正+升职加薪+跳槽技巧」。
点这里去了解,剑指大厂吧!
今天来介绍一款工具Squaretest,它是一款自动生成单元测试的插件,为什么会用到它?
主要因为最近公司上了代码质量管控的指标,会考评各个项目的单元测试覆盖率,以及sonar扫描出来的各种问题,很多老项目老代码,或者着急交付的项目,单元测试严重缺失,覆盖率只有5%不到。
所以几个小伙伴这几天就在疯狂的堆单元测试,3个人堆了2天才堆到30%,于是我也来上手帮忙写了两个,写到第二个的时候就发现,这个活不应该是人干的,要去看原来的代码,然后根据逻辑写各种Mock,感觉是有迹可循的东西,所以就查了下,发现果然有插件帮我们来干这个事情,那么解下来就来看看。
我使用的是idea,我们先来下载一下插件,
File——>Settings——>Plugins
,搜索
Squaretest
,然后
install
就好了,插件安装完成后需要重启一下
重启之后,菜单栏就多了一项
Squaretest
,下面我们来讲下怎么用,大家也可以通过看这个菜单的最后一项:
Generate Test Methods(Help)
来看它的一个演示,但演示不太全,我下面截图给大家看下我怎么用的,以及一些使用心得。
首先我们打开一个类,这个类就是我们即将要作为实验的类,这个类有7个public方法,因为
Squaretest
生成的单元测试方法都是只能生成public的,当然这也是合理的嘛!毕竟private的肯定被public调用了。
如果我们来手写这个类的单元测试,光看都要一会,下面看我操作,打开你的类,光标定位到代码里,右击鼠标选择Generate…
然后你就会看到这里有两个熟悉的图标,第一次的话选择第二个选项,它会让你选择你一下单元测试的模板,因为我已经选择过了,所以我现在演示不回再弹出,但后面我会告诉你怎么更改模板。
自动会使用类的真实目录层次在test文件夹中创建出来一个单元测试类,类名就是原类名后加Test
我把代码贴出来给大家看看它生成出来的是什么样的,看看吓不吓人,牛逼牛逼,7个单元测试方法,秒秒钟就出来了,各位看官你们自己写要多久能写出来,毕竟时间就是金钱啊!然后我们执行一把试试!
public
class
CrawlerScreenShotServiceImplTest
{
@Mock
private
CrawerScreenShotTaskMapper mockCrawerScreenShotTaskMapper;
@Mock
private
CrawerScreenShotTaskLogMapper mockCrawerScreenShotTaskLogMapper;
@InjectMocks
private
CrawlerScreenShotServiceImpl crawlerScreenShotServiceImplUnderTest;
@Before
public
void
setUp
{
initMocks(
this
);
@Test
public
void
testReceiveData
{
// Setup
final
CrawlerScreenShotVO vo =
new
CrawlerScreenShotVO;
vo.setUrl(
"url"
);
vo.setPcFlag(
false
);
vo.setMembergroup(
"membergroup"
);
vo.setTaskType(
0
);
vo.setUrlType(
0
);
when(mockCrawerScreenShotTaskLogMapper.saveSelective(any(CrawerScreenShotTaskLog
.
class
))).
thenReturn
(0)
;
when(mockCrawerScreenShotTaskMapper.saveBatch(Arrays.asList(
new
CrawlerScreenShotTask(
0L
,
"url"
,
"imageOssUrl"
,
false
,
false
,
"memberGroup"
,
0
,
0
,
"fileName"
,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime,
false
,
"skuCode"
,
"state"
,
"operater"
)))).thenReturn(
0
);
// Run the test
final
Result<String> result = crawlerScreenShotServiceImplUnderTest.receiveData(vo);
// Verify the results
@Test
public
void
testListJobScreenShotTask
{
// Setup
// Configure CrawerScreenShotTaskMapper.listJobScreenShotTask(...).
final
CrawlerScreenShotTaskDto crawlerScreenShotTaskDto =
new
CrawlerScreenShotTaskDto;
crawlerScreenShotTaskDto.setId(
0L
);
crawlerScreenShotTaskDto.setUrl(
"url"
);
crawlerScreenShotTaskDto.setSkuCode(
"skuCode"
);
crawlerScreenShotTaskDto.setPcFlag(
false
);
crawlerScreenShotTaskDto.setMemberGroup(
"memberGroup"
);
crawlerScreenShotTaskDto.setUrlType(
0
);
crawlerScreenShotTaskDto.setFileName(
"fileName"
);
crawlerScreenShotTaskDto.setTaskType(
0
);
crawlerScreenShotTaskDto.setState(
"state"
);
final
List<CrawlerScreenShotTaskDto> crawlerScreenShotTaskDtos = Arrays.asList(crawlerScreenShotTaskDto);
when(mockCrawerScreenShotTaskMapper.listJobScreenShotTask(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime)).thenReturn(crawlerScreenShotTaskDtos);
// Run the test
final
List<CrawlerScreenShotTaskDto> result = crawlerScreenShotServiceImplUnderTest.listJobScreenShotTask;
// Verify the results
@Test
public
void
testQuery
{
// Setup
final
NikeScreenShotListRequestVo requestVo =
new
NikeScreenShotListRequestVo;
requestVo.setUrl(
"url"
);
requestVo.setUrlType(
0
);
requestVo.setStartTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
requestVo.setEndTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
requestVo.setStatus(
0
);
requestVo.setPcFlag(
0
);
requestVo.setPageNum(
0
);
requestVo.setPageSize(
0
);
// Configure CrawerScreenShotTaskMapper.query(...).
final
PimScreenShotVo pimScreenShotVo =
new
PimScreenShotVo;
pimScreenShotVo.setId(
0L
);
pimScreenShotVo.setUrl(
"url"
);
pimScreenShotVo.setImageOssUrl(
"imageOssUrl"
);
pimScreenShotVo.setStatus(
0
);
pimScreenShotVo.setPcFlag(
false
);
pimScreenShotVo.setCreateTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
pimScreenShotVo.setUrlType(
0
);
pimScreenShotVo.setMsg(
"msg"
);
final
List<PimScreenShotVo> pimScreenShotVos = Arrays.asList(pimScreenShotVo);
when(mockCrawerScreenShotTaskMapper.query(any(NikeScreenShotListRequestVo
.
class
))).
thenReturn
(
pimScreenShotVos
)
;
// Run the test
final
PageInfo<PimScreenShotVo> result = crawlerScreenShotServiceImplUnderTest.query(requestVo);
// Verify the results
@Test
public
void
testQuerySelectBoxData
{
// Setup
// Configure CrawerScreenShotTaskMapper.query(...).
final
PimScreenShotVo pimScreenShotVo =
new
PimScreenShotVo;
pimScreenShotVo.setId(
0L
);
pimScreenShotVo.setUrl(
"url"
);
pimScreenShotVo.setImageOssUrl(
"imageOssUrl"
);
pimScreenShotVo.setStatus(
0
);
pimScreenShotVo.setPcFlag(
false
);
pimScreenShotVo.setCreateTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
pimScreenShotVo.setUrlType(
0
);
pimScreenShotVo.setMsg(
"msg"
);
final
List<PimScreenShotVo> pimScreenShotVos = Arrays.asList(pimScreenShotVo);
when(mockCrawerScreenShotTaskMapper.query(any(NikeScreenShotListRequestVo
.
class
))).
thenReturn
(
pimScreenShotVos
)
;
// Run the test
final
PimScreenShotTaskParamsDto result = crawlerScreenShotServiceImplUnderTest.querySelectBoxData;
// Verify the results
@Test
public
void
testFindExecutionScreenShotTaskCount
{
// Setup
when(mockCrawerScreenShotTaskMapper.findExecutionScreenShotTaskCount).thenReturn(
0
);
// Run the test
final
Integer result = crawlerScreenShotServiceImplUnderTest.findExecutionScreenShotTaskCount;
// Verify the results
assertEquals(
0
, result);
@Test
public
void
testFindCrawerScreenshotTaskByCreateTime
{
// Setup
final
CrawlerScreenShotTaskSyncDto crawlerScreenShotTaskSyncDto =
new
CrawlerScreenShotTaskSyncDto;
crawlerScreenShotTaskSyncDto.setId(
0L
);
crawlerScreenShotTaskSyncDto.setUrl(
"url"
);
crawlerScreenShotTaskSyncDto.setSkuCode(
"skuCode"
);
crawlerScreenShotTaskSyncDto.setTaskType(
0
);
crawlerScreenShotTaskSyncDto.setStatus(
0
);
crawlerScreenShotTaskSyncDto.setLastModifyTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
crawlerScreenShotTaskSyncDto.setOperater(
"operater"
);
crawlerScreenShotTaskSyncDto.setMsg(
"msg"
);
final
List<CrawlerScreenShotTaskSyncDto> expectedResult = Arrays.asList(crawlerScreenShotTaskSyncDto);
// Configure CrawerScreenShotTaskMapper.findCrawerScreenshotTaskByCreateTime(...).
final
CrawlerScreenShotTaskSyncDto crawlerScreenShotTaskSyncDto1 =
new
CrawlerScreenShotTaskSyncDto;
crawlerScreenShotTaskSyncDto1.setId(
0L
);
crawlerScreenShotTaskSyncDto1.setUrl(
"url"
);
crawlerScreenShotTaskSyncDto1.setSkuCode(
"skuCode"
);
crawlerScreenShotTaskSyncDto1.setTaskType(
0
);
crawlerScreenShotTaskSyncDto1.setStatus(
0
);
crawlerScreenShotTaskSyncDto1.setLastModifyTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
crawlerScreenShotTaskSyncDto1.setOperater(
"operater"
);
crawlerScreenShotTaskSyncDto1.setMsg(
"msg"
);
final
List<CrawlerScreenShotTaskSyncDto> crawlerScreenShotTaskSyncDtos = Arrays.asList(crawlerScreenShotTaskSyncDto1);
when(mockCrawerScreenShotTaskMapper.findCrawerScreenshotTaskByCreateTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime)).thenReturn(crawlerScreenShotTaskSyncDtos);
// Run the test
final
List<CrawlerScreenShotTaskSyncDto> result = crawlerScreenShotServiceImplUnderTest.findCrawerScreenshotTaskByCreateTime(
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
// Verify the results
assertEquals(expectedResult, result);
@Test
public
void
testQueryCrawlerDashboard
{
// Setup
when(mockCrawerScreenShotTaskMapper.queryCrawlerDashboard(
0
,
0
,
0
,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime)).thenReturn(
0
);
// Run the test
final
Integer result = crawlerScreenShotServiceImplUnderTest.queryCrawlerDashboard(
0
,
0
,
0
,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime,
new
GregorianCalendar(
2019
, Calendar.JANUARY,
1
).getTime);
// Verify the results
assertEquals(
0
, result);
报错了呢,不要慌,这个断言是为了检查你单元测试跑出来的结果是否符合预期的,如果你不想检查只想完成覆盖率,直接干掉就可以了(手动狗头)。
怎么样!刺不刺激,爽不爽,秒秒钟90多行的代码覆盖率就到了90%以上.
上面说过第一次进来会让你选择单元测试的模板,如果你要切换的话可以在单元测试类中按快捷键,Alt+M,或者通过
Squaretest
的菜单倒数第二个,下面这个就是按快捷键的效果,我选择的是这个模板,你们也可以借鉴。
OK,以上
Squaretest
部分就结束了,当然拉也不能高兴的太早,这个类算是比较成功的情况,很多时候还是要你自己小修小改的,毕竟它生成出来的测试数据可能完全匹配不上你的
if else
数据对吧,但这都很好改啊,这样就从自己分析if else变成了,debug程序了呀,哪里报错,debug过去,看看是不是生成的数据有问题,改个数据,就通过了,反正本人用的是很舒畅的,妥妥的节省70%的工作量。
解决了上面一个问题之后,又发现另一个问题,这个工具
VO,DTO,Entity,Command,Model
这种实体类来讲,一般这种实体类我们都用lombok的注解
get,set
,还有constract构造器等注解,但是这个工具只能生成这些实体类的构造器的单元测试,无法生成
get set
方法的单元测试,所以写了个base方法,实体类继承一下,简单的写两行带就好了,看下面代码:
@SpringBootTest
@RunWith
(MockitoJUnitRunner
.
class
)
public
abstract
class
BaseVoEntityTest
<
T
> {
protected
abstract
T
getT
;
private
void
testGetAndSet
throws
IllegalAccessException, InstantiationException, IntrospectionException,
InvocationTargetException {
T t = getT;
Class modelClass = t.getClass;
Object obj = modelClass.newInstance;
Field[] fields = modelClass.getDeclaredFields;
for
(Field f : fields) {
boolean
isStatic = Modifier.isStatic(f.getModifiers);
// 过滤字段
if
(f.getName.equals(
"isSerialVersionUID"
) || f.getName.equals(
"serialVersionUID"
) || isStatic || f.getGenericType.toString.equals(
"boolean"
)
|| f.isSynthetic) {
continue
;
PropertyDeor pd =
new
PropertyDeor(f.getName, modelClass);
Method get = pd.getReadMethod;
Method set = pd.getWriteMethod;
set.invoke(obj, get.invoke(obj));
@Test
public
void
getAndSetTest
throws
InvocationTargetException, IntrospectionException,
InstantiationException, IllegalAccessException {
this
.testGetAndSet;
同样的方式我们在实体类上通过
Squaretest
生成单元测试,然后继承我上面写的那个base类,vo的单元测试代码稍加改动,如下
看run完之后,覆盖率100%,妥妥的,通过这两个解决方案,一天之内我们就把覆盖率搞到了60%以上,不要太刺激,大家可以用用试试哦,当然这个也不是纯为了应付差事写的单元测试,我们后续开发的时候,也可以用这个工具来生成,然后自测自己的代码,这样也是提升工作效率的嘛!
欢迎加入我的知识星球,一起剑指大厂,不断晋升
加薪。
剑指大厂不仅是一个获取信息的圈子,还是一个规划职业的导师。已在知识星球,更新如下 点这里去了解,剑指大厂吧! (
或点击下图了解
):
//////
END
//////
↓ 点击下方关注,看更多架构分享 ↓
返回搜狐,查看更多
责任编辑:
平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。