小码奔腾

记录一些和自动化测试、CI有关的想法


  • Home

  • Archives

在云服务器上使用Python+Selenium+Jenkins搭建webUI测试

Posted on 2018-05-29

在Digitalocean.com上租了个Ubuntu 16.04的云服务器,用优惠码可以新用户送15刀,大约是最小配置的服务器可以用3个月,而且是按分钟算钱,除了网速有时候可能慢点,实在是没有其他缺点了。

有云服务器后,还需要安装以下一些软件:

  • Firefox, 普通的sudo apt-get install firefox会安装最新版本的Firefox,正确的做法是去Mozilla的服务器上找指定的版本,然后用wget下载到服务器里去,我这次是用的版本46的安装包,wget https://ftp.mozilla.org/pub/firefox/releases/46.0/linux-x86_64/en-US/firefox-46.0.tar.bz2,再tar -jxvf firefox-46.0.tar.bz2解压缩,得到一个firefox目录,可以放到/lib目录中,再生成一个soft link文件ln -s /lib/firefox/firefox /usr/bin/firefox,最后在任意目录下执行firefox -version正常输出版本的话就ok了。
  • Xvfb, sudo apt-get install xvfb,解决没有主机没有屏幕可能会导致的无法连接Firefox的异常。
  • geckodriver, Firefox浏览器的webdriver驱动,需要去Github-geckodriver-releases,比如下载0.15.0版本的geckodriver,wget到主机上后,添加执行权限,再mv到/usr/bin目录。
  • Jenkins, JDK。
  • Jenkins安装Xvfb Plugin,还需要去Jenkins的全局工具设置中配置Xvfb的安装目录,在webUI job里需要配置启动Xvfb。
  • pip install安装Python3、pytest、selenium等依赖。

搭建过程中遇到最耗时间的还是geckodriver和Firefox版本匹配的问题,最开始安装的最新的60的Firefox和最新版本的geckodriver,几次尝试后发现应该是geckodriver支持不了这么高版本的Firefox,然后对Firefox进行降级,最后geckodriver 0.15.0和Firefox 46.0适配成功。

至此,环境配置部分基本完成了,如果Selenium代码ok的话应该是可以正常运行,后续打算继续给这套测试添砖加瓦,完善test case基类,页面对象,静态代码检查等工作。

使用Git制作patch文件

Posted on 2018-05-27

项目中是用Review board来做code review,用Git来管理代码,所以每次提交之前,需要把修改的代码做成patch文件,上传到Review board里去,再发给相关同事检查。

这里简单记录下制作patch文件的过程。

假设大家一直工作在master分支上,现在当前目录下的repo已经git pull到最新,同时需要提交的代码已经完成,检查git status。

1
2
3
4
5
6
7
8
9
rx:pytest_proj reed$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)

change.txt

nothing added to commit but untracked files present (use "git add" to track)

执行git branch patch生成一个名为patch的分支,再git checkout patch切换到patch分支。

在patch分支上git add .,继续git commit,完成commit后再执行git format-patch -M master生成patch文件,其含义是将当前分支上的,所有的,更加新的提交(和master分支相比)打包成patch文件,然后可见当前目录下会生成一个.patch文件。

然后这个patch文件即可上交了,最后需要把当前repo拆回原状,注意当前分支还是patch分支,需要先get reset --soft 上次commit的hash,再继续git reset HEAD .,最后切回最开始工作的master分支即可。

Java中的Mock测试框架Mockito

Posted on 2018-05-27

上周无意中了解到一个很有意思的Java mock测试套件Mockito,简单研究了下,感觉拿来写单元测试和集成测试应该很好用,而且用起来也很方便,话不多说,直接看代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.mockedtesting;

import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.*;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.junit.Test;

public class MockedObjectUnitTest {

@Test
public void testMock() {
List mockedList = mock(List.class);

mockedList.add("one");
mockedList.add("two");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
when(mockedList.size()).thenReturn(100);
assertEquals(mockedList.size(), 100);

// as you see, load and clear
verify(mockedList, atLeastOnce()).add("one");
verify(mockedList, times(1)).add("two");
verify(mockedList, times(3)).add("three times");
verify(mockedList, never()).isEmpty();
}

@Test
public void createMockObject() {
List mockedList = mock(List.class);
assertTrue(mockedList instanceof List);

ArrayList mockedArrayList = mock(ArrayList.class);
assertTrue(mockedArrayList instanceof List);
assertTrue(mockedArrayList instanceof ArrayList);
}

@Test
public void configMockObject() {
List mockedList = mock(List.class);

// CONFIG: when calling mockedList.add("one"), return true
when(mockedList.add("one")).thenReturn(true);

// CONFIG: when calling mockedList.size(), return 1
when(mockedList.size()).thenReturn(1);

assertTrue(mockedList.add("one"));
// Because there's no config for mockedList.add("two"), false is
// returned as default value
assertFalse(mockedList.add("two"));
assertEquals(mockedList.size(), 1);

Iterator i = mock(Iterator.class);

// CONFIG: when calling i.next(), return "Hello," at 1st time,
// return "Mockito" at 2nd time
when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");
String result = i.next() + " " + i.next();
assertEquals("Hello, Mockito!", result);
}

@Test(expected = NoSuchElementException.class)
public void testForException() throws Exception {
Iterator i = mock(Iterator.class);
when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");
String result = i.next() + " " + i.next();
assertEquals("Hello, Mockito!", result);

// CONFIG: throw exception when calling i.next() for the 3rd time,
// which means only 2 elements in i.
doThrow(new NoSuchElementException()).when(i).next();
i.next(); // now it is the 3rd time to call i.next()
}
}

构建一个有Web Application的container

Posted on 2018-05-27

课程中的例子中是用Flask框架的web app,大致步骤如下。

首先把代码clone下来,执行git clone git@github.com:jleetutorial/dockerapp.git

有Dockerfile如下:

1
2
3
4
5
6
7
FROM python:3.5
RUN pip install Flask==1.0.2
RUN useradd -ms /bin/bash admin
USER admin
WORKDIR /app
COPY app /app
CMD ["python", "app.py"]

有app\app.py,一个Flask的web app如下:

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'

if __name__ == '__main__':
app.run(host='0.0.0.0')

在app.py的上层目录执行docker build -t dockerapp:v0.1 .,构建一个docker image.

执行docker images来查询本机上所有的docker image,可以发现dockerapp已经在列表中了。

1
2
3
rx:dockerapp reed$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerapp v0.1 1661743950e7 About a minute ago 700MB

运行一个dockerapp的container,-d表示在后台运行,-p表示端口映射,执行结果返回container ID。

1
2
rx:app reed$ docker run -d -p 5000:5000 1661743950e7
2312dc563731677c5b127628df2312889bd8d9b3104560d56154a0c0498f13c0

然后可以用docker ps来查询运行中的container列表。

1
2
3
rx:app reed$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2312dc563731 1661743950e7 "python app.py" 2 minutes ago Up 2 minutes 0.0.0.0:5000->5000/tcp dreamy_torvalds

打开本机浏览器访问http://localhost:5000/,即可看到web app已经在运行,并显示Hello, World!。

进入后台中运行的container(interactive模式)并执行一些命令

1
2
3
4
5
6
7
8
9
rx:app reed$ docker exec -it 2312dc563731 bash
admin@f5f0334b04a0:/app$ pwd
/app
admin@f5f0334b04a0:/app$ cd /home/admin/
admin@f5f0334b04a0:~$ ps axu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
admin 1 0.0 1.2 252100 25340 ? Ss 11:49 0:00 python app.py
admin 9 0.1 0.1 21956 3676 pts/0 Ss 11:54 0:00 bash
admin 16 0.0 0.1 19188 2380 pts/0 R+ 11:54 0:00 ps axu

学到这里感觉docker应用很广啊,至少在配置自动化测试环境上应该很好使。

Docker Technologies for DevOps and Developers学习笔记 - 2

Posted on 2018-05-20

Build custom docker image

使用docker commit命令来创建docker image

General steps:

  • Spin up a container from a base image.
  • Install Git package in the container.
  • Commit changes made in the container.

把Docker国内镜像https://registry.docker-cn.com添加至docker registry-mirrors。

或者可直接用命令拉取镜像,例如docker pull registry.docker-cn.com/library/ubuntu:16.04

国内镜像配置好后,执行docker run -it debian:jessie,拉取debian镜像并进入container。

在debian container后,执行apt-get update && apt-get install -y git,在container中安装Git。

安装完成后执行exit退出,再执行docker images -a查找刚刚安装了git的debian container的ID。

执行docker commit GIT_DEBIAN_CONTAINER_ID reed/debian:1.00来新建一个安装了Git的debian docker image,完成后可使用docker images来查看本地image列表。

完成以上后,就可以在本地执行docker run -it reed/debian:1.00 来新建一个debian container并进入,可以发现container中已经安装好了Git。

使用Dockerfile来创建docker image

以debian:jessie为base image,新建一个安装了Git和vim的image,以下是Dockerfile。

1
2
3
4
FROM debian:jessie
RUN apt-get update
RUN apt-get install -y git
RUN apt-get install -y vim

然后执行docker build -t reed/gitvim/debian .,表示在当前目录下搜索Dockerfile,新image的REPOSITORY名字为reed/gitvim/debian。

但是,实践中可能会遇到因为网络等问题引起的安装不顺利,感觉还是直接用commit命令来手动创建custom image比较合适。

Good Practice -> Dockerfile也可这样写:

1
2
3
4
FROM debian:jessie
RUN apt-get update && apt-get install -y \
git \
vim

-> 避免重复的package同时让列表易于更新:

1
2
3
4
5
FROM debian:jessie
RUN apt-get update && apt-get install -y \
git \
python \
vim

-> 另一个例子,CMD中的命令仅当container运行的时候执行

1
2
3
4
5
6
FROM debian:jessie
RUN apt-get update && apt-get install -y \
git \
python \
vim
CMD ["echo", "hello docker"]

-> 为保证apt-get update是最新的,运行Dockerfile的时候可以关闭docker cache -> docker build -t reed/debian . --no-cache=true

-> CMD中加入copy命令,复制文件到container中去,下面Dockerfile生成的container将会从当前目录中复制文件

1
2
3
4
5
6
FROM debian:jessie
RUN apt-get update && apt-get install -y \
git \
python \
vim
COPY abc.txt /src/abc.txt

-> CMD中还有ADD命令,ADD可以理解是升级版的COPY,可以从网上下载文件,也可以自动化解压缩文件,通常优先使用COPY,除非非常肯定没问题的情况下则使用ADD。

Docker Technologies for DevOps and Developers学习笔记 - 1

Posted on 2018-05-19

Concepts

Images

  • which are read only templates used to create containers.
  • are created with the docker build command by docker user.

Containers

  • container is an instance of an image(like an instance of a class - a runtime object)

Official Docker images website -> https://hub.docker.com/

Basic commands

ls本机上的docker image -> docker images

创建一个有busybox的的docker container 并执行 echo "hello docker" -> docker run busybox:1.24 echo "hello docker"

ls docker container中的根目录 -> docker run busybox:1.24 ls /

进入docker container -> docker run -i -t busybox:1.24,执行exit可退出container,每一次都是一个新的image

使用docker ps -a来查看创建过的历史container ->

1
2
3
4
5
rx:dev reed$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d46a47c46f34 busybox:1.24 "sleep 1000" 3 minutes ago Up 3 minutes wonderful_shockley
180a27bc3ba4 busybox:1.24 "sh" 4 minutes ago Exited (0) 4 minutes ago nostalgic_ritchie
901dec3242bb busybox:1.24 "ls /" 26 minutes ago Exited (0) 26 minutes ago pensive_keldysh

新建一个container执行命令,完成后自动删除这个container -> docker run --rm busybox:1.24 sleep 1

新建一个指定名字的docker container -> run --name my_docker_container busybox:1.24 ls

inspect一个container的底层信息 ->

1
2
3
rx:dev reed$ docker run -d busybox:1.24 sleep 100
7dd3d07d1ae16e0ccd042c62cc08a2523dc3dbf325ea9f957ff49fcf1e414645
rx:dev reed$ docker inspect 7dd3d07d1ae16e0ccd042c62cc08a2523dc3dbf325ea9f957ff49fcf1e414645

拉取tomcat 8.0 image,并创建一个container,同时将container的8080端口映射到主机的8888端口 ->

  • 从国内docker image源中下载tomcat 8.0 image -> docker pull registry.docker-cn.com/library/tomcat:8.0
  • 创建container并映射端口 -> docker run -it -p 8888:8080 registry.docker-cn.com/library/tomcat:8.0
  • 运行container并在后台运行 -> docker run -it -d -p 8888:8080 registry.docker-cn.com/library/tomcat:8.0 执行后会显示container ID
  • 查看后台运行的container的运行log -> docker logs DOCKER_CONTAINER_ID
  • 停止一个后台运行的container -> docker stop DOCKER_CONTAINER_NAME

Run Container in Foreground or Background

run container in foreground -> default mode

run container in background -> -d(dispatch) option

建一个container在后台运行,并用ps查看运行中的container ->

1
2
3
4
5
rx:dev reed$ docker run -d busybox:1.24 sleep 1000
d46a47c46f34cbec69dc6ce04e9674a74f81d5bea17b93a8d441c4a88c8ba056
rx:dev reed$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d46a47c46f34 busybox:1.24 "sleep 1000" 1 second ago Up 2 seconds wonderful_shockley
123…6
Reed Xia

Reed Xia

记录一些和自动化测试、CI有关的想法

33 posts
32 tags
GitHub E-Mail
© 2018 Reed Xia
Hosted by GitHub Pages