专栏首页软件测试那些事Mock老中医的2则验方

Mock老中医的2则验方

最近有个开发同学过来求助说某个系统接受的时候,发现里面的代码几乎没有单元测试,只是对几个DTO做了set/get的测试!看能不能帮忙指导下怎么开展。代码pull下来看了看,写了个demo,顺便解决了两个Mock方面的问题,提交上去供开发同学继续写用例。

问题1:static block 静态代码块

这是第一个遇到的问题。笔者按照一般套路做好Mockito的三板斧之后开始跑用例,结果发现执行失败。 一排查结果发现,这服务还使用了JNI。

@Service
@Transactional
public class BookServiceImpl implements BookService {
    static {
        System.out.println("static block called");
        System.loadLibrary("libOnlyRunOnLinux.so");
    }

抛以下的错误

java.lang.UnsatisfiedLinkError: no libOnlyRunOnLinux.so in java.library.path:

这是一个连接JNI和java的adapter服务。因为开发测试是在Windows上码代码,而上游开发只提供了so包,然后就跑挂了。 一开始,建议开发同学可否提供根据runtime来动态加载so或者dll。咨询一番后被告知,这个so包是另外一个team提供的,短期内改不了。 那就退而求其次吧,是不是可以避免使用静态块,而是使用类似@PostConstruct的方式来提供。

    @PostConstruct
    loadJNI(){
        System.out.println("static block called");
        System.loadLibrary("libOnlyRunOnLinux.so");
    }

结果又被告知,之前试过,貌似会导致core,所以才使用的静态块。 于是,问题就变成了如何来绕过这个so包的导入,反正单测的时候哦这个服务是要被mock掉的。 关于静态的东西,Mockito就搞不定了,得请出Powermock了。

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("com.xpinjection.springboot.service.BookServiceImpl")
public class BookServiceImplTest 

通过这个@SuppressStaticInitializationFor来如何阻止静态代码块的执行呢。 当然引入Powermock的时候,要注意和使用的Mockito的版本匹配问题。 第一个问题搞定。

问题2:@Autowired Gson

在一个服务类中使用了gson,不过是通过@Autowired方式依赖注入的。

    @Autowired
    Gson gson ;

然后又在方法中使用了。请忽略,随便找了一个网上的demo, 加的这两行代码。

    @Override
    public List<Book> addBooks(Map<String, String> books) {
        gson.toJson(books);
        return books.entrySet().stream()
                .map(entry -> new Book(entry.getKey(), entry.getValue()))
                .map(bookDao::save)
                .collect(toList());
    }

这个时候就会遇到一个两难的问题。假设不对Gson进行mock, 以下的用例就会抛空指针

public class BookServiceImplTest {
    @Mock
    private BookDao dao;

    private BookService bookService;

    @Before
    public void init() {
        bookService = new BookServiceImpl(dao);
    }

    @Test
    public void ifNoBooksPassedEmptyListIsReturned() {
        assertThat(bookService.addBooks(Collections.emptyMap()), is(empty()));
    }

因为Gson的实例化是Spring托管的

java.lang.NullPointerException
    at com.xpinjection.springboot.service.BookServiceImpl.addBooks(BookServiceImpl.java:37)

如果在测试用例中添加一个

    @Mock
    Gson gson ;

又需要我们给出各种stub。由于Gson在这里是个底层方法,不是应用的其它服务,理论上不应该被mock,就像我们不太会去mock StringUtil类似的。 那怎么解决呢?

先试试

    @Spy
    Gson gson ;

然后再造一个测试桩

        when(gson.toJson(any())).thenCallRealMethod();

思路是说,当使用json的时候,需要使用真实的,而不是mock的实例。然而,

org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'gson'.

Cannot mock/spy class com.google.gson.Gson
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types

因为Gson是一个final classes,因此不能被mock。 那咋办呢?还是得拿出Spring测试框架提供的依赖注入工具 org.springframework.test.util.ReflectionTestUtils

   Gson gson = new Gson();
    private BookService bookService;
    @Before
    public void init() {
        bookService = new BookServiceImpl(dao);
        ReflectionTestUtils.setField(bookService, "gson", gson);
    }

首先,还是正常来new 一个Gson实例,然后通过ReflectionTestUtils将这个实例注入到bookService的私有变量gson。这样,后续的gson在调用时就可以获取到一个可以正常使用的gson了。 小绿条又回来了。

本文分享自微信公众号 - 软件测试那些事(antony-not-available),作者:风月同天测试人

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用MockMVC进行Controller单元测试

    由于MockMVC是Spring框架自带的测试组件,因此只要在项目中引入spring-boot-starter-test这个测试套件就可以使用Spring-te...

    Antony
  • Jacoco字节码插桩案例

    1 注入方式 JaCoCo是一个被广泛使用的JAVA覆盖率统计工具,它利用ASM库,通过注入字节码的方式来修改和生成java字节码,从而记录程序的执行数据,但它...

    Antony
  • 录制回放实现测试用例自由

    定义了 @Pointcut("execution(public * io.metersphere..controller...(..))") 这个切面,表...

    Antony
  • 拥抱更底层技术——从CSS变量到Houdini 0. 前言1. CSS变量2. CSS type OM3. paint API4. 自定义属性最后

    平时写CSS,感觉有很多多余的代码或者不好实现的方法,于是有了预处理器的解决方案,主旨是write less &do more。其实原生css中,用上css变量...

    lhyt
  • python用paramiko模块上传本地目录到远程目录

    Java学习123
  • 前端综合面试题(第二期)

    1.script 的位置是否会影响首屏显示? 不影响开始时间,但影响结束时间 2.disiplay 与 visibility的区别? v-if 与 v-...

    我不是费圆
  • 拥抱更底层技术——从CSS变量到Houdini

    平时写CSS,感觉有很多多余的代码或者不好实现的方法,于是有了预处理器的解决方案,主旨是write less &do more。其实原生css中,用上css变量...

    IMWeb前端团队
  • Android中的AES加密-下

    本章主要是针对AES加密的原理过程进行梳理。不在于细节,了解各个参数和类的功能来帮助我们再加密是更好的选择如何使用加密。

    g小志
  • 自然语言处理之Skip-Gram的预测算法

    自然语言处理属于人工智能领域,它将人类语言当做文本或语音来处理,以使计算机和人类更相似,是人工智能最复杂的领域之一。 由于人类的语言数据格式没有固定的规则和条理...

    商业新知
  • 国内首家外资公募诞生!资管巨头贝莱德全资持有,注册资本3个亿

    根据最新证监会披露公告显示,贝莱德基金管理有限公司(以下简称贝莱德基金)核准成立。注册资本为3亿元人民币,张弛任法定代表人兼总经理。据媒体报道,这是证监会批复的...

    量化投资与机器学习微信公众号

扫码关注云+社区

领取腾讯云代金券