所有由admin发布的文章

微信小程序中使用微信风格样式库-WeUI

原文地址:https://www.jianshu.com/p/f4e3212c9ca7

WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含button、cell、dialog、 progress、 toast、article、actionsheet、icon等各式元素。

WeUI

WeUI的官方演示地址是:https://weui.io/
WeUI的官方GitHub地址是:https://github.com/weui

WeUI最初是为HTML5的页面设计的样式库,现在微信小程序使用的样式机制也是css风格的样式机制(不过稍稍有些不一样的地方),WeUI理所当然的推出了WXSS版本。它的GitHub地址是:https://github.com/weui/weui-wxss

我们可以直接去Github上下载它的代码,也可以通过npm来方便的获取:

npm install weui-wxss

下载下来的代码中,包含了weui的源代码,以及一个基于weui的示例小程序:

示例小程序

里面包含了丰富的使用weui的示例代码,可以很好的学习weui提供的各个样式的使用。

我们可以通过微信web开发者工具来打开这个示例代码:

示例代码
button样式

你的小程序主要是通过在你的app.wxss中导入weui.wxss来导入WeUI的样式的:

@import "style/weui.wxss";

之后,就可以在小程序的各个地方使用WeUI中定义的样式了。

作者:一斤代码
链接:https://www.jianshu.com/p/f4e3212c9ca7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

mysql重连,连接丢失:The last packet successfully received from the server

原文地址:http://blog.csdn.net/dxswzj/article/details/42778031

问题原因:

其实上面的提示中已经给出了一部分的简要说明,简单来说就是: 程序启动时,在跟DB首次交互时,获得了相应的DB Connection资源,从而进行正常的DB读写操作。但是在下次进行DB读写时(我的定时任务本身设置的时间间隔是24小时),应用程序认为这个连接是可以正常使用的(程序执行过一次之后没有退出,这个连接从来并没有被释放掉),但实际上,这个连接已经坏掉了,因为Mysql本身已经把这个连接标记为timeout了。于是,应用程序“傻乎乎”的在这个已经坏掉的数据通道上发起对DB的读写请求,但是Mysql已经对这些请求不买账了。。。

 

为啥呀,mysql到底认为这个连接空闲多长时间算过期啊?

这个可以通过查看mysql的配置文件,看看是否有对这个时间做过特殊的配置,我的场景下在64位linux服务器上部署的mysql服务器,这个配置文件在:在/etc/my.cnf

如果你打开这个文件,发现并没有如下这行配置:

 

Xml代码  收藏代码
  1. wait_timeout=xxx (这里xxx是数据,单位为秒)

说明你并没有对这个timeout做过特殊配置,通常Mysql默认的配置是8小时。你也可以在登陆进入mysql之后,通过如下命令确认一下:

 

Sql代码  收藏代码
  1. show global variables like ‘wait_timeout’;

结合上面我的程序的配置(24小时执行一次),上面的问题就好解释了:24小时之后程序再次对DB进行读写操作时,Mysql单方已经认为之前connection已经timeout了(停止活动了8小时以上就认为过期了)。

如何解决:

1.1 错误信息:

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 20,820,001 milliseconds ago.  The last packet sent successfully to the server was 20,820,002 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
		at sun.reflect.GeneratedConstructorAccessor29.newInstance(Unknown Source) ~[na:na]
		at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.7.0_51]
		at java.lang.reflect.Constructor.newInstance(Constructor.java:526) ~[na:1.7.0_51]
		at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1129) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3988) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2598) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:5372) ~[mysql-connector-java-5.1.29.jar:na]
		at com.mchange.v2.c3p0.impl.NewProxyConnection.setAutoCommit(NewProxyConnection.java:881) ~[c3p0-0.9.1.1.jar:0.9.1.1]
		at org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler.setAutoCommit(AttributeRestoringConnectionInvocationHandler.java:98) ~[quartz-2.2.1.jar:na]

1.2 解决方法

– 如果使用的是JDBC,在JDBC URL上添加?autoReconnect=true,如:

jdbc:mysql://10.10.10.10:3306/mydb?autoReconnect=true

– 如果是在Spring中使用DBCP连接池,在定义datasource增加属性validationQuerytestOnBorrow,如:

<bean id="vrsRankDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${countNew.jdbc.url}" />
    <property name="username" value="${countNew.jdbc.user}" />
    <property name="password" value="${countNew.jdbc.pwd}" />
    <property name="validationQuery" value="SELECT 1" />
    <property name="testOnBorrow" value="true"/>
</bean>

– 如果是在Spring中使用c3p0连接池,则在定义datasource的时候,添加属性testConnectionOnCheckintestConnectionOnCheckout,如:

<bean name="cacheCloudDB" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${cache.url}"/>
    <property name="user" value="${cache.user}"/>
    <property name="password" value="${cache.password}"/>
    <property name="initialPoolSize" value="10"/>
    <property name="maxPoolSize" value="${cache.maxPoolSize}"/>
    <property name="testConnectionOnCheckin" value="false"/>
    <property name="testConnectionOnCheckout" value="true"/>
    <property name="preferredTestQuery" value="SELECT 1"/>
</bean>

 

网页加载慢浏览器端分析方法

  1. 打开Chrome浏览器,访问要分析的网页
  2. 按下F12键,或在页面上点击右键选择“检查元素”
  3. 弹出的面板中,点击Network标签,选中里面的“Disable Cache”选项(目的是禁用缓存,否则不能体现首次访问加载所有资源的情况)
  4. 刷新网页,面板左下角会显示加载页面共请求了多少资源,共耗费多少时间,共使用了多少流量。并以表格形式显示出所有的网络资源请求,每一行一个,每行都包括请求该资源所耗费的时间、资源大小等信息。
  5. 下一步进行分析,主要从两个方面,一是请求时间,二是资源大小。
  6. 对于请求时间特别长的资源,可以看下是否该资源为国外资源,因为被墙导致访问时间长。
  7. 点击Size,按资源大小进行倒序排列,找到占用空间大的资源。

C语言Socket请求Apache服务器一直报HTTP/1.1 400 Bad Request

用C语言写了个Socket程序,发送HTTP请求到Apache服务器,一直报HTTP/1.1 400 Bad Request。尝试了无数方法,依然无果。

最终发现还是HTTP协议头的格式问题,主要就在于\r\n的问题。

把所有的\n都换成了\r\n之后,终于可以了。

注意:如果是POST请求,POST数据部分的上下要多加一个空行,也就是\r\n\r\n

Linux中如何让进程到后台运行?Linux里面程序一直运行不被中断

本文主要来自https://fukun.org/archives/06281192.html 和 http://blog.csdn.net/wangjun_1218/article/details/6835800

外加一点自己的实践。

原文如下:

https://fukun.org/archives/06281192.html

在Linux中,如果要让进程在后台运行,一般情况下,我们在命令后面加上&即可,实际上,这样是将命令放入到一个作业队列中了:

Example
1
2
3
4
5
$ ./test.sh &
[1] 17208
$ jobs -l
[1]+ 17208 Running                 ./test.sh &

对于已经在前台执行的命令,也可以重新放到后台执行,首先按ctrl+z暂停已经运行的进程,然后使用bg命令将停止的作业放到后台运行:

Example
1
2
3
4
5
6
7
8
$ ./test.sh
[1]+  Stopped                 ./test.sh
bg %1
[1]+ ./test.sh &
$ jobs -l
[1]+ 22794 Running                 ./test.sh &

但是如上方到后台执行的进程,其父进程还是当前终端shell的进程,而一旦父进程退出,则会发送hangup信号给所有子进程,子进程收到hangup以后也会退出。如果我们要在退出shell的时候继续运行进程,则需要使用nohup忽略hangup信号,或者setsid将将父进程设为init进程(进程号为1)

Example
1
2
3
4
5
6
7
8
9
echo $$
21734
nohup ./test.sh &
[1] 29016
ps -ef | grep test
515      29710 21734  0 11:47 pts/12   00:00:00 /bin/sh ./test.sh
515      29713 21734  0 11:47 pts/12   00:00:00 grep test
Example
1
2
3
4
5
6
$ setsid ./test.sh &
[1] 409
ps -ef | grep test
515        410     1  0 11:49 ?        00:00:00 /bin/sh ./test.sh
515        413 21734  0 11:49 pts/12   00:00:00 grep test

上面的试验演示了使用nohup/setsid加上&使进程在后台运行,同时不受当前shell退出的影响。那么对于已经在后台运行的进程,该怎么办呢?可以使用disown命令:

Example
1
2
3
4
5
6
7
8
9
10
11
$ ./test.sh &
[1] 2539
$ jobs -l
[1]+  2539 Running                 ./test.sh &
$ disown -h %1
ps -ef | grep test
515        410     1  0 11:49 ?        00:00:00 /bin/sh ./test.sh
515       2542 21734  0 11:52 pts/12   00:00:00 grep test

另外还有一种方法,即使将进程在一个subshell中执行,其实这和setsid异曲同工。方法很简单,将命令用括号() 括起来即可:

Example
1
2
3
4
5
$ (./test.sh &)
ps -ef | grep test
515        410     1  0 11:49 ?        00:00:00 /bin/sh ./test.sh
515      12483 21734  0 11:59 pts/12   00:00:00 grep test

注:本文试验环境为Red Hat Enterprise Linux AS release 4 (Nahant Update 5),shell为/bin/bash,不同的OS和shell可能命令有些不一样。例如AIX的ksh,没有disown,但是可以使用nohup -p PID来获得disown同样的效果。

还有一种更加强大的方式是使用screen,首先创建一个断开模式的虚拟终端,然后用-r选项重新连接这个虚拟终端,在其中执行的任何命令,都能达到nohup的效果,这在有多个命令需要在后台连续执行的时候比较方便:

Example
1
2
3
4
5
6
7
8
screen -dmS screen_test
screen -list
There is a screen on:
        27963.screen_test       (Detached)
1 Socket in /tmp/uscreens/S-jiangfeng.
screen -r screen_test
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
现在说说我的问题和解决方法:
我想在Ubuntu服务器上挂载一个socket通信的小程序,是一个server,我想让这个程序一直运行着。可是我发现一旦没有客户端来连接它,没几分钟,改程序自动关闭。这是个令人头疼的问题。
根据上面指导,解决方法如下:
输入指令:

echo $$

nohup ./tcp_demo &

ps -ef |grep tcp_demo

说明:tcp_demo是要运行的程序名称

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
原文如下:
http://blog.csdn.net/wangjun_1218/article/details/6835800
PS: 其实这并不是原文地址,这个地址的文章也是转载的,不过我发现他附的原文地址不可访问了,所以姑且把这个当做是原文吧
nohup、setsid、&和disown
2010-04-02 15:16
在执行命令时,偶尔会碰到需要让对应进程无间断执行的情况。前段时间看到IBM官网的一篇文章,由于不让转载(原文位置:IBM),遂简单记录如下:

我们知道,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

1. nohup
显而易见,nohup命令的功能是使进程忽略hangup信号,从而持续执行。nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上”&”来将命令同时放入后台运行,也可用”>filename 2>&1″来更改缺省的重定向文件名。
如:nohup myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &
这样起到了三个效果:进程后台执行;忽略hangup信号;输出重定向。

2. setsid
setsid中的sid指的是session id,意指以该命令运行的进程是一个新的session,因此其父进程id不属于当前终端。实际上,setsid运行的进程,其父进程id(PPID)为1(init 进程的 PID)。因此,setsid解决问题用的是第二种途径。
如:setsid myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &
格式与nohup相仿,后台运行也需加上&,但输出重定向必须手动设置。

3. 括号()与&
&代表后台运行(注意输出并没有被重定向);此外,我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。
当我们将”&”也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。
如:(myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &)
以这种方式运行程序,新提交的进程的父 ID为1,并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

4. disown
如果我们未加任何处理就已经提交了命令,这时想加 nohup 或者 setsid 已经为时已晚,只能通过作业调度和 disown 来解决这个问题了。让我们来看一下 disown 的帮助信息:
disown [-ar] [-h] [jobspec …]
Without options, each jobspec is  removed  from  the  table  of
active  jobs.   If  the -h option is given, each jobspec is not
removed from the table, but is marked so  that  SIGHUP  is  not
sent  to the job if the shell receives a SIGHUP.  If no jobspec
is present, and neither the -a nor the -r option  is  supplied,
the  current  job  is  used.  If no jobspec is supplied, the -a
option means to remove or mark all jobs; the -r option  without
a  jobspec  argument  restricts operation to running jobs.  The
return value is 0 unless a jobspec does  not  specify  a  valid
job.
可以看出,我们可以用如下方式来达成我们的目的。
a. 用disown -h jobspec 来使某个作业忽略HUP信号。
b. 用disown -ah 来使所有的作业都忽略HUP信号。
c. 用disown -rh 来使正在运行的作业忽略HUP信号。
需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。
但是还有一个问题,这种方法的操作对象是作业,如果我们在运行命令时在结尾加了”&”来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过jobs命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!
CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs命令来查询它的作业号,再用bg jobspec 来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,慎用此方法。
示例:
root@ubuntu-server:/mnt/ftp# jobs
[1]+  Running                 ./sync_mirror.sh &
root@ubuntu-server:/mnt/ftp# disown -h %1
root@ubuntu-server:/mnt/ftp# ps -ef|grep sync_mirror.sh
root      8650 22271  0 15:08 pts/0    00:00:00 grep sync_mirror.sh
root     16748 22271  0 Mar31 pts/0    00:00:00 /bin/sh -e ./sync_mirror.sh

另外还有一个命令screen。screen 提供了 ANSI/VT100 的终端模拟器,使它能够在一个真实终端下运行多个全屏的伪终端。screen 的参数很多,具有很强大的功能。本篇不做介绍。

Android Studio自动生成对应分辨率的图标icon launcher

使用 Android Studio ,在 module或者 drawable 上右键 new > ImageAsset ,可以把一张大图自动裁切成各 DPI 对应的分辨率的 icon launcher。

细节操作参照下图:
create icon launcher

  1. 选择要生成的图片类型:Launcher Icons ;
  2. Name 会自动生成 ic_launcher ,如果不是则填成 ic_launcher ;
  3. Asset Type 设置成 Image ;
  4. 在 path 一列的最后选择自己要生成的图片
  5. 调整 Shape 等参数;
  6. 点击 next 一路到 finish 即可。

PS:使用 Shape 的其他参数,比如圆角square , 会默认使用不支持透明度的颜色作为背景,所以要根据具体的图标进行修改,特殊的情况需要设计师切出带圆角和透明度的大图。

非root的crontab调用失败

感觉守护一直没作用,开始以为是守护脚本有问题。重试多次觉得很OK。实在不行找了root用户,看日志。

Feb 11 18:19:01  crond[27831]: (CRON) ERROR (setreuid failed): Resource temporarily unavailable
Feb 11 18:19:01  crond[27831]: (xxx) ERROR (failed to change user)

crontab一直启动失败。资源不足,但是ulimited又很大。

root但是看的一些守护进程limits里边又很小。重启一下crond,进程的limits上去了,但是还是失败。

最后修改了 /etc/security/limits.d/90-nproc.conf

*          soft    nproc     1024
root       soft    nproc     unlimited

1024改成unlimited,问题解决了。

OCX注册时找不到指定的模块

开发好的OCX用regsvr32注册时,经常会报“找不到指定的模块”的错误。这个错误一般是由于缺少系统依赖造成的。可根据下面的步骤进行排查解决。

1. 错误原因:OCX路径错误

仔细检查OCX路径是否输入正确。

2. 错误原因:缺少系统依赖

安装微软常用运行库,可百度搜索“微软常用运行库合集”,也可以通过下面的链接直接下载。

32位:MSVBCRTAIO_X86.zip

64位:MSVBCRTAIO_X64.zip

3. 错误原因:OCX版本不对

尝试修改VS中的目标平台位数,修改为64位或32位,再尝试注册。

4. 错误原因:缺少其它依赖

下载Depends.exe,将OCX拖入至窗口,查看缺少的依赖项。

5. 错误原因:OCX存放位置

网上有文章说,将OCX移至System32或SysWow64目录下进行注册。若以上方法均不可用,也可尝试此方法。

ERR: SOAP-ERROR: Parsing WSDL: Couldn’t load from XXX解决方案

原文链接:http://myitlife.blog.51cto.com/2289784/1529621

PHP在用SOAP协议做接口的时候,经常会碰到如下问题,不是不成功,而是偶尔不成功,实在让人费解!

ERR: SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://www.xxxxx.com/member/member_sync.php?wsdl’ : failed to load external entity “http://www.xxxxx.com/member/member_sync.php?wsdl”

查找日志发现:

NOTIC: [2] SoapClient::SoapClient(): I/O warning : failed to load external entity “http://www.xxxxx.com/member/member_sync.php?wsdl” /home/wwwroot/default/xxxxx.com/App/Modules/Admin/Action/OrderAction.class.php 第 112 行.客户端$client = new SoapClient ( $url );创建SoapClient对象时出错!

网上查找很多资料,有开启selinux、关闭soap缓存、开启openssl等等答案,但尝试过后都不尽人意,现终极解决办法:

只需把php的客户端SoapClient的wsdl模式修改为non-wsdl模式,即可解决!

wsdl模式:

$url = “http://xxxxx.org/member/member_sync.php?wsdl”;

$client = new SoapClient ( $url );

non-wsdl模式:

$client = new SoapClient(null,array(‘location’=>’http://www.xxxxx.com/member/member_sync.php’,uri => ‘member_sync.php’));

Windows环境下安装phpunit

参考文章:http://blog.csdn.net/sunshinelyc/article/details/49834293#comments

进来由于工作需要,安装phpunit,在网上找了各种安装方法,pear添加phpunit通道安装,手动安装,各种失效,各种无语,皆因网上的资料都是年份比较久远的(也有可能本人不太了解phpunit的工作原理),导致安装了一个下午进展缓慢。最后在phpunit.de中看到了一篇文章,然后第二天再试着安装了一次,具体成功了(然后也对phpunit有了一个比较基础的了解)故此,记录一下本人安装phpunit的过程,方便自己和路人查阅,该文章仅作为本人查阅之用,故此希望网友看到了不要吐槽。。微笑

话不多说,直接开展。。。

首先,官网文档连接如下phpunit.de ,如果是由于年份久远导致无法连接的只能再找找资料了(再网上找资料的时候就碰到了一大推年份久远导致链接失效的情况。。。大哭,导致坑越来越深。。。)

安装步骤如下:

其实安装过程非常非常非常简单。。(注意用了三个非常,足以证明强调意义)

1、在官网下载phpunit.phar包,链接如下:phpunit.phar,下载和自己php版本对应的稳定包即可。(注意:这里是PHPUnit 的 PHP 档案包,它将 PHPUnit 所需要的所有必要组件(以及某些可选组件)捆绑在这个文件中)

2、创建一个文件夹,这里我在d盘创建了phpunit,路径为D:\phpunit,将该路径添加到path环境变量中,然后将下载好的phpunit.phar放到该文件夹中

3、打开一个cmd窗口,按照如下执行(每个人的cmd路径不一样,不要对号入座。。):

C:\Users\Administrator>d:

D:\>cd phpunit

D:\phpunit>echo @php “%~dp0phpunit.phar” %* > phpunit.cmd

D:\phpunit>exit

4、重新打开一个新的cmd窗口,在cmd中执行执行phpunit –version

C:\Users\Administrator>phpunit –version
PHPUnit 4.8.18 by Sebastian Bergmann and contributors.

这样子就表示你的phpunit已经安装完成了。。是不是超级简单。。不到半个小时就搞定的事情,我昨天研究了一个下午。。也是够蠢的。。。(不要局限于用pear安装。安装方法各种各样。结果达到就行。。),暂时介绍安装。后续继续补充phpunit的学习过程,毕竟这东西,才有一点点概念。至于怎么使用。原理是什么还是一头雾水。。

Zend Framework单元测试方法

Update: Things are all change in Zend Framework 1.8, so this may require some adaptation in that version.

If you’ve followed the Zend Framework quickstart tutorial like me, you may be wondering how to perform unit testing using it. The tutorial does set up the bootstrap file to allow for unit testing, but does not cover unit testing itself. The aim of this post is to  provide a quick guide to unit testing using the quickstart application, which will hopefully come in useful for a few people and myself when I inevitably forget this later on.

Step 0: Before Starting

First, you should download and set up the guestbook application from the Zend Framework website. Make sure you place the Zend library in the library folder and enable the read/write permissions for the sqlite database and the db directory for access by the web server.

You will also want to install pear and, with it, install PHPUnit:

pear channel-discover pear.phpunit.de

pear install phpunit/PHPUnit

Step 1: Create Test Folders

Create a folder called “tests” in the root folder of the application. Inside that create a folder called “controllers” and “models”. The  directory structure should be as follows:

zendquickstart
|-- application
|-- data
|-- library
|-- public
|-- scripts
`--tests
 |-- controllers
 `-- models

All your controller tests should go in the controllers folder, all your model tests should go in the models folder.

Step 2: Create Test Configuration

Create the file TestConfiguration.php in the tests folder. This file will initially set up the testing configuration, inside place the following code:

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
<?php
// Gets called when this file is included/required
TestConfiguration::setUp();

class TestConfiguration
{
    /**
    * Sets the environment up for testing
    */
    static function setUp()
    {
        // Set the environment constant to testing which will load the testing
        // configuration in app.ini by the bootstrap
        define('APPLICATION_ENVIRONMENT', 'testing');

        // Set the include path for locating the Zend library
        set_include_path(realpath(dirname(__FILE__)) . '/../library'
            . PATH_SEPARATOR . get_include_path());

        // Use Autoload so that we don't have to include/require every class
        require_once "Zend/Loader.php";
        Zend_Loader::registerAutoload();
    }

    static function setUpDatabase()
    {
        require '../application/bootstrap.php';

        $db = Zend_Registry::get('configuration')->database->params->dbname;

        // delete any pre-existing databases
        if(file_exists($db)) unlink($db);

        // run the database set up script to recreate the database
        require '../scripts/load.sqlite.php';
    }
}
?>

The setUp() function in this class will get called when the file is included or required. The APPLICATION_ENVIRONMENT constant is set to ‘testing’ so that the testing section of the app.ini file is read in the bootstrap, and the testing database is used. A testing database should be used so that testing does not interfere with the production or development databases, and no important  data is lost. The next step is to set the include path, which should contain the location of the Zend Framework library. The final step is to enable the Zend autoloader so that we don’t have to explicitly require/include the Zend libraries.

The setUpDatabase() function resets the database to a known state. When the tests are run we may wish to insert data to the database and this should be removed before every new test. We require the bootstrap file so we can get the database configuration and delete the database file, which is an easy way to reset an SQLite database. A different method will be necessary for other databases like MySQL or PostgreSQL. Finally, the setUpDatabase() function uses a script which comes with the quick start application to (re)create the database.

Step 3: Creating Controller Tests

To test the IndexController, create the file IndexControllerTest.php in the tests/controller directory. The following is a simple example:

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
<?php
// Set up the testing environment
require 'TestConfiguration.php';

class controllers_IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
    // Bootstraps the application
    public $bootstrap = '../application/bootstrap.php';

    public function testHomePageIsASuccessfulRequest()
    {
        // Runs the test on /, the homepage
        $this->dispatch('/');

        // Tests there are no exceptions on the home page
        $this->assertFalse($this->response->isException());

        // Tests for redirection to the error handler
        $this->assertNotRedirect();
    }

    public function testHomePageDisplaysCorrectContent()
    {
        // Runs the test on /
        $this->dispatch('/');

        // Tests the page title is present
        $this->assertQueryContentContains(
            'div#header-logo',
            'ZF Quickstart Application'
        );

        // Tests the guestbook link is present
        $this->assertQueryContentContains('a', 'Guestbook');
    }
}
?>

This is a very basic controller test. First note that you should extend Zend_Test_PHPUnit_ControllerTestCase for controller tests. This class extends PHPUnit_Framework_TestCase itself, but adds some assertions and other things specific to Zend Framework.

The bootstrap instance variable must be set in order to test the controller, and in this case it points to the location of the bootstrap file.

The testHomePageIsASuccessfulRequest() test is used to test that the homepage functions correctly; it should not contain any exceptions or redirect to the error controller. If it does then there is a problem somewhere and the test fails. The testHomePageDisplaysCorrectContent() is used to test that the page title and the link to the guestbook is present.

Step 4: Creating Model Tests

To test the GuestBook model, create the file GuestBookTest.php in tests/models.

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
<?php
require 'TestConfiguration.php';
require '../application/models/GuestBook.php';

class models_GuestBookTest extends PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        // Reset database state
        TestConfiguration::setUpDatabase();
    }

    public function testFetchEntries()
    {
        // Instantiate the GuestBook model
        $guestBook = new Model_GuestBook();

        // Get all entries from the database
        $entries = $guestBook->fetchEntries();

        // Test that there are 2 entries in the guestbook
        $this->assertSame(2, count($entries));
    }
}
?>

Again, this is very basic. The setUp() function gets called by PHPUnit before the test is run, and it will reset the database. The testFetchEntries() function tests the fetchEntries() function in the model. Two rows in the guestbook table should be present, therefore there should be two elements in the array returned by fetchEntries().

Step 5: Edit load.sqlite.php

You may find it annoying to have to wait 5 seconds between model tests when the database is recreated. I worked around this problem by making a small change to the PHP script which loads the database schema:

1
2
3
4
5
6
7
8
9
<?php
if (APPLICATION_ENVIRONMENT != 'testing')
{
    echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
    for ($x = 5; $x > 0; $x--) {
        echo $x . "\r"; sleep(1);
    }
}
?>

If the application environment is not testing then the timeout should not be displayed. You may prefer to simply remove the timeout altogether. Additionally, you will need to change the lines that locate the SQL files to include dirname(FILE):

1
2
3
4
<?php
$schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
$dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
?>

Step 6: Running the Tests

To run the tests, navigate to the tests directory in a terminal/CMD, and type phpunit controllers_IndexControllerTest and models_GuestBookTest to test the IndexController and GuestBook model, respectively.

phpunit controllers_IndexControllerTest

PHPUnit 3.3.14 by Sebastian Bergmann.

..

Time: 0 seconds

OK (2 tests, 4 assertions)

phpunit models_GuestBookTest

PHPUnit 3.3.14 by Sebastian Bergmann.

Database Created

Data Loaded.

.

Time: 0 seconds

OK (1 test, 1 assertion)

Conclusion

There you have it. This isn’t necessarily the only way or the best way to perform unit testing, this is merely the way I got it to work with the quickstart application having had no prior experience with PHPUnit. Any suggestions or improvements are welcome.

Credits

I based this on the Zend Framework manual and the book Zend Framework in Action.

Unity导出Android工程Gradle时填坑

Player Settings中选择Use Existing Keystore文件后,可以输入Keystore password,但是Confirm keystore password却无法输入。

这个时候点击Key下面的Alias下拉框,提示Choose Android SDK root folder,按说我的Android SDK目录是/Users/xxxx/Library/sdk,但诡异的一点就是无法找到该目录,但是终端中是可以找到的。

后来百度了一下,在选择目录时按下快捷键shift+command+g,然后输入“/Users/xxxx/Library/”就可以找到该目录了。然后Key下面的Alias下拉框终于可以下拉了。