首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mock老中医的2则验方

Mock老中医的2则验方

作者头像
Antony
发布2020-12-03 14:56:03
1.3K0
发布2020-12-03 14:56:03
举报

最近有个开发同学过来求助说某个系统接受的时候,发现里面的代码几乎没有单元测试,只是对几个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了。 小绿条又回来了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件测试那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题1:static block 静态代码块
  • 问题2:@Autowired Gson
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档