3

问题一

报错如下:
图片.png
搜索后发现此类错误和数据库的字符集有关系之所以之前没发现此类问题是因为之前测试时只测试了保存英文字符,然而当我们存储中文字符时就会出现此类错误。

很显然我们建库时并没有注意到这些,并且在当前环境下默认建库并不支持中文。

所以要解决此类错误只需要修改一下字符集即可。
图片.png
或者将我们的url改为如下重建一次数据库

jdbc:mysql://localhost:3310/process-evaluation?useUnicode=true&characterEncoding=utf-8

图片.png

虽然说在本地解决很简单,但是如果只是这样做的话在远程机器人运行时还会发生报错,因为仅仅改变链接时的url链接并不能直接改变现有数据库的配置。
图片.png

那么我么就需要像在本地那样重新配置字符集或是重新建立数据库,那么我们就需要了解远端机器人的数据库是怎么建立的。

我们在提PR时在什么环境下跑pipeline,按什么顺序,要进行什么操作,这些配置都和.gitlab-ci.yml文件相关
图片.png
之前已有篇文章讲过其大致配置方法,在此就不过多赘述。

spring-test:
  tags:
    - docker
  stage: unit-test
  image: maven:3.6-openjdk-8
  services:
    - name: mysql:5.7
      alias: mysql
      variables:
        MYSQL_ROOT_PASSWORD: root
        MYSQL_DATABASE: process-evaluation

  .  .  .

  before_script:
    - cd api
  script:
    - env
    - mvn -v
    - mvn package
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'

其中tags定义了哪些runner适用该job,我们当前使用的是docker环境,其中的image和我们之前配置docker时遇到的相同,也指的是映像,所以就需要docker环境。

图片.png
这些是官方对其的说明——当你配置CI/CD时,可以用image创建一个容器当job运行时,但是image只能有一个,如果还需要其他image就需要用service来指定。service可以创建另一个容器,并且这两个容器间可相互通信。

有了这些我们运行后台所需要的环境就只差mysql了,又因为mysql需要配置所以就采用service进行引进。

官方也给出了相应的配置方法

services:
  - mysql:latest
  variables:
  # Configure mysql environment variables (https://hub.docker.com/_/mysql/)
  MYSQL_DATABASE: $MYSQL_DATABASE
  MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD

其中的service项可以使用Docker Hub中任何docker映像,例如,要使用 MySQL 5.5,请使用mysql:5.5。

并且imgge可接受除此以外的其他环境变量,具体变量和docker hub中对mysql的说明相同。

但是上面这些并不能解决我们遇到的问题——怎么改变数据库的字符集?
docker环境变量并不支持配置字符集,后来又发现官方文档中也写出了怎么运行mysql通过shell。

图片.png
但是到第一步就出现了问题,当前环境并不支持执行apt install,image也不能进行变化,通过tags的运行环境也不能随意更改,毕竟我们也需要docker环境。

所以又尝试单独运行一个database-init的job,在上面进行这些操作,但是每个job之间又都是独立的,也就是说我们在database-init中初始化完了数据库,等此job结束后这个容器就被释放了,我们之后再用的又是一个全新的容器。所以我们只能在service中作改动,但是按照官方文档中所说,service中只能配置环境变量。

除此之外还有另一种方法就是自己写一个dockerfile,初始化一个字符集为utf8的docker5.7的映像再通过阿里云构建,最后再在service中引入。

这样的话虽然可以解决但也会遇到其他问题,并且后来我们可以得知在service中作改动就可以解决此问题。再此就不做展开。
下面是老师给出的解决方法:
图片.png

可以直接通过command来进行操作。虽然问题解决了,但是新的问题也来了——为什么我找不到可以这么解决,官方文档上也没找到这样的操作。
后来又仔细查看了官方文档,发现确实有对command的解释:
图片.png
应用作容器命令的命令或脚本。它被翻译成在镜像名称之后传递给 Docker 的参数。
总接下来还是看官方文档不仔细,这是官方文档的菜单。
图片.png
之前把注意力全部集中在mysql service中,当看到其中可以通过安装 mysql-client来执行命令时就想当然认为想要执行相应命令就只有这一个方法。
总结:官方文档还是要仔细看,不能自己想什么就是什么,可能官方文档的编排和我们平时阅读习惯不同,但是当遇到问题时还是要相信官方文档。

问题二 单元测试时出现问题

图片.png
出现问题的行:

        org.assertj.core.api.Assertions.assertThat(resultUser).isEqualTo(mockResultUser1);

先来看一下mockResultUser1是怎么来的

UserService userServiceSpy = Mockito.spy(this.userService);
UserServiceImpl userServiceImplSpy = (UserServiceImpl) userServiceSpy;
User mockResultUser1 = new User();
Mockito.doReturn(mockResultUser1).when(userServiceImplSpy).updateNameAndEmailField(Mockito.any(User.class), Mockito.anyString(), Mockito.anyString());

我们设定当调用updateNameAndEmailField时返回mockResultUser1。

resultUser为null,那我们再来看看resultUser从哪来的。

 User user = new User();
 User resultUser = userServiceImplSpy.updateNameAndEmail(id, user);
    @Override
    public User updateNameAndEmail(Long id, User user) {
        User oldUser = this.userRepository.findById(id).get();
        return this.updateNameAndEmailField(oldUser, user.getName(), user.getEmail());
    }

    public User updateNameAndEmailField(User oldUser, String name, String email) {
        oldUser.setName(name);
        oldUser.setEmail(email);
        return this.userRepository.save(oldUser);
    }

明白了大体流程接下来就是打点,观察为什么断言不成立,那一部和我们预期不同。
图片.png
我们可以看到在最外层传参都是没问题的,并且resultUser也确实为null。
那么就要进一步区service层中打点。
图片.png
updateNameAndEmaile中也没有问题。

图片.png
到这里我们就能发现问题了,email为null,这也就说明了我们没有对email设初值,但是执行过程中没有发生报错,说明这也是可以的,比如我们当前情况下也可以将email设置为null,因为我们的报错只是因为断言不正确。

那么为什么会返回null呢,主要还是因为下面:

Mockito.doReturn(mockResultUser1).when(userServiceImplSpy).updateNameAndEmailField(Mockito.any(User.class), Mockito.anyString(),Mockito.anyString());

我们设置当传给updateNameAndEmailField(user,String,String)时才会返回mockResultUser1,但是null并不在Mockito.anyString()的范围里,所以才会返回null,如果我们改为Mockito.any()就不会出现上面的断言错误。

当然我们还是应该按照原先的思想,给email设定初始值,并且最好在updateNameAndEmailField中加入断言。、

        Assert.notNull(email, "email 不能为null");

这样我们在执行单元测试时就可以很清楚的知道哪里出了问题。
图片.png


李明
441 声望18 粉丝