数据库测试

本文介绍了如何使用PHPUnit进行数据库测试,包括环境配置、数据库测试的四个阶段(清理、建立基境、运行测试和拆除基境)、设置连接方式、初始数据的设置(如XML、CSV和Array数据集)以及运行监测和验证结果的方法。重点关注了数据库连接、数据初始化和断言API的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

8 数据库测试

PHPUnit支持的访问数据库:

  • MySQL
  • PostgreSQL
  • Oracle
  • SQLite
  • IBM DB2
  • Microsoft SQL Server

当然,对应的拓展需要安装好,比如pdo

环境配置

官网上没有介绍这里,所以这里是我自己搭建环境过程中找到的一些问题。

首先,composer默认帮你安装的是最新版本的PHPUnit,现在是8.0.1,但是phpunit/dbunit只能在PHPUnit7.*版本中使用,所以你可能需要重装PHPUnit,这就是属于composer的范畴,我是最简单把vendor目录和composer.jsoncomposer.lock文件删除了,接着运行下面的命令来安装的:

composer require phpunit/phpunit 7.5.x-dev

再来安装dbUnit

composer require phpunit/dbunit

但是他报了一个警告:

Package phpunit/dbunit is abandoned, you should avoid using it. No replacement was suggested.

好像是说这个包不再维护了,应该尽量避免使用它。但是我们这里只是学习,先用着吧。

数据库测试的四个阶段

清理数据库

简单来说,PHPUnit会对指定的表进行一次truncate操作,将表清空。如何指定表接下来会讲。

建立基境

上面清除数据后,PHPUnit会讲你需要的数据插入到数据库中。 插入的方法下面会讲。

运行测试,验证结果,并拆除基境

这一部分就是靠你自己写代码判断数据是否正确插入了,但是和我原先预期不一样的是,我原本以为是靠我们编写待测试的代码来插入数据,其实是PHPUnit设定了很多数据类型,来插入这些指定的数据,所以和原先感觉有点不一样。

而最后的拆除基境,其实PHPUnit不会再对对应表执行一次truncate操作。

下面来看一个实际的demo

设置连接方式

基础版

require_once __DIR__."/../../vendor/autoload.php";

use PHPUnit\Framework\TestCase;


class DataBaseTest extends TestCase{
   
    use PHPUnit\DbUnit\TestCaseTrait;
    protected $databaseType="mysql";
    protected $host="127.0.0.1";
    protected $user="root";
    protected $password="1234qwer";
    protected $databaseName="test";

    /**
     * 该方法会在 setUp 中调用一次
     */
    protected function getDataSet()
    {
   
        // TODO: Implement getDataSet() method.      
    }

    protected function getConnection()
    {
   
        // TODO: Implement getConnection() method.
        $dsn=$this->databaseType.":host=".$this->host.";dbname=".$this->databaseName;
        $pdo=new PDO($dsn,$this->user,$this->password);
        return $this->createDefaultDBConnection($pdo);
    }
}

这里的重点就是:use PHPUnit\DbUnit\TestCaseTrait;需要实现它的两个抽象方法:

trait的相关知识可以参考我这篇博客,里面介绍的很详细

  • getDataSet():这个方法需要返回一个指定的数据库初始数据,这个下一节再讲。
  • getConnection():这个方法需要返回一个指定的数据库连接方式

这里是拿mysql为例,借助PDO拓展进行连接,如果是其他类型的数据库的话,比如Orcale,则也可以使用PDO,只是配置修改一下而已。$this->createDefaultDBConnection($pdo);则是返回一个PHPUnit需要的连接类型。

书上的例子是建议我们将这一层专门提取出来写在一个类里面,这样就不用在每个类中重复了,因为一般情况下我们测试时不会测试多个数据库。

再来就是数据库地址,账号密码等信息了,这里是使用硬编码的方式写在类中了,PHPUnit支持多个配置文件,所以也可以写在配置文件中:

使用配置文件修改连接方式

配置文件的demo

<?xml version="1.0" encoding="UTF-8" ?>
<phpunit>
    <php>
        <var name="DB_DSN" value="mysql:dbname=test;host=127.0.0.1" />
        <var name="DB_USER" value="root" />
        <var name="DB_PASSWD" value="password" />
        <var name="DB_DBNAME" value="test" />
    </php>
</phpunit>

这里的namevalue都不是固定的,这里我们是借用PHPUnit的一个功能,在这里设定的值都会被写进$GLOBALS中,如下所示:

require_once __DIR__."/../../vendor/autoload.php";

use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;

class DataBaseXMLTest extends TestCase{
   
    use TestCaseTrait;
    private static $pdo=null;// 单例模式
    private $conn=null;// 这个值是我们人为设定的
    protected function getDataSet()
    {
   
        // TODO: Implement getDataSet() method.
    }
		// 这里我们
    final protected function getConnection()
    {
   
        if($this->conn==null){
   
            if(self::$pdo==null){
   
                self::$pdo=new PDO($GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']);
            }
            $this->conn=$this->createDefaultDBConnection(self::$pdo,$GLOBALS['DB_DBNAME']);
        }
        return $this->conn;
    }
}

可以看到这里我们其实是从$GLOBALS中读取数据,所以假设我们有2个配置文件:

  • databaseSet1.xml
  • databaseSet2.xml

那么当我们在命令行中使用参数:--configuration指定配置文件时,我们就可以动态修改数据库的连接方式了:

phpunit --configuration databaseSet1.xml DataBaseXMLTest.php
phpunit --configuration databaseSet2.xml DataBaseXMLTest.php

设置初始数据

简单来说有以下几种方式:

  • 基于给定文件的
  • 基于数据库查询的
  • 基于筛选和组合的

基于文件的

Flat XML Dataset

例子:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
    <guestbook
package com.bjsxt.servlet; import com.bjsxt.entity.User; import com.bjsxt.service.UserService; import com.bjsxt.service.impl.UserServiceImpl; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.*; import java.io.IOException; import java.net.URLEncoder; import java.sql.Date; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class UserServlet extends BaseServlet { // @Override // protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // //解决POST表单的中文乱码问题 // request.setCharacterEncoding("utf-8"); // //接收method属性的值 // String methodName = request.getParameter("method"); // // //根据method属性的值调用相应的方法 // if("login".equals(methodName)){ // this.login(request,response); // }else if("register".equals(methodName)){ // this.register(request,response); // }else if("logout".equals(methodName)){ // this.logout(request,response); // } // // } public void show(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取表单的数据 String userId = request.getParameter("userId"); if(userId == null){ userId = ""; } String strAge = request.getParameter("minAge"); int minAge = 0; try{ minAge = Integer.parseInt(strAge); //"12" "abc" }catch(NumberFormatException e){ e.printStackTrace(); } //调用业务层完成查询操作 UserService userService = new UserServiceImpl(); //List<User> userList = userService.findAll(); List<User> userList = userService.find(userId,minAge); //List<User> userList = null; //List<User> userList = new ArrayList<User>(); //跳转到show.jsp显示数据 request.setAttribute("userId",userId); request.setAttribute("minAge",strAge); request.setAttribute("ulist",userList); request.getRequestDispatcher("/admin/show.jsp").forward(request,response); } public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //结束当前的session request.getSession().invalidate(); //跳转回登录页面 response.sendRedirect(request.getContextPath()+"/admin/login.jsp"); } public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //request.setCharacterEncoding("utf-8"); //1.接收来自视图层的表单数据 String userId = request.getParameter("userId"); String realName = request.getParameter("realName"); String pwd = request.getParameter("pwd"); String rePwd = request.getParameter("repwd"); int age = Integer.parseInt(request.getParameter("age"));// "23" String [] hobbyArr = request.getParameterValues("hobby"); String strDate = request.getParameter("enterDate");//"1999-12-23" Date enterDate = Date.valueOf(strDate); //util.Date SimpleDateFormat //判断两次密码是否相同 if(pwd == null || !pwd.equals(rePwd)){ request.setAttribute("error","两次密码必须相同"); request.getRequestDispatcher("/admin/register.jsp").forward(request,response); return; } //2.调用业务层完成注册操作并返回结果 User user = new User(userId,realName,pwd,age, Arrays.toString(hobbyArr),enterDate); UserService userService = new UserServiceImpl(); int n = userService.register(user); //3.根据结果进行页面跳转 if(n>0){ response.sendRedirect(request.getContextPath()+"/admin/login.jsp"); }else{ request.setAttribute("error","注册失败"); request.getRequestDispatcher("/admin/register.jsp").forward(request,response); } } public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解决POST表单的中文乱码问题 //request.setCharacterEncoding("utf-8"); //获取用户名和密码 request 内建对象 请求 String username = request.getParameter("username"); String password = request.getParameter("password"); String rememberme = request.getParameter("rememberme"); //调用下一层判断登录是否成功,并返回结果 //进行服务器端的表单验证 if(username ==null || "".equals(username)){ request.setAttribute("error","用户名不能为空JSP"); request.getRequestDispatcher("/admin/login.jsp").forward(request,response); return; } if (username.length()<=6){ request.setAttribute("error","用户名长度必须大于6JSP"); request.getRequestDispatcher("/admin/login.jsp").forward(request,response);//后面语句还会执行 return; //后面的语句不再执行 } // boolean flag = false;//默认失败 // if(username.indexOf("sxt")>=0 || username.contains("尚学堂")){ // flag = true; // } User user = null;//默认登录失败 // UserDao userDao = new UserDaoImpl(); // user = userDao.find(username,password); UserService userService = new UserServiceImpl(); user = userService.login(username,password); //userService.addOrder("shoppingCart"); //输出结果 if(user != null){ //登录成功才记住我 //1.办理会员卡 String username2 = URLEncoder.encode(username,"utf-8"); Cookie cookie1 = new Cookie("uname",username2); Cookie cookie2 = new Cookie("password",password); //2.指定会员卡的作用范围,默认范围是当前目录 /servlet/LoginServlet /admin/login.jsp //cookie1.setPath("/"); //当前服务器 cookie1.setPath("/myservlet2/"); //当前项目 cookie2.setPath("/myservlet2"); //3.指定会员卡的作用时间 if("yes".equals(rememberme)){ cookie1.setMaxAge(60*60*24*10); //默认的时间浏览器不关闭的时间;-1 表示一直有效 cookie2.setMaxAge(60*60*24*10); }else{ cookie1.setMaxAge(0); cookie2.setMaxAge(0); } //4.将会员卡带回家 response.addCookie(cookie1); response.addCookie(cookie2); //成功跳转到成功页面 //out.println("登录成功"); // /servlet/LoginServlet // /servlet/success.jsp // request.getRequestDispatcher("/admin/success.jsp").forward(request,response); HttpSession session = request.getSession(); // session.setAttribute("username",username); session.setAttribute("user",user); //response.sendRedirect("/myservlet2/admin/success.jsp"); //response.sendRedirect("https://www.bjsxt.com:443/news/11377.html"); //response.sendRedirect("http://localhost:8080/myservlet2/admin/success.jsp"); //response.sendRedirect("/myservlet2/admin/success.jsp"); //response.sendRedirect("/myservlet2/admin/success.jsp"); //response.sendRedirect(request.getContextPath()+"/admin/success.jsp"); //http://192.168.58.250:8080/myservlet2/servlet/LoginServlet //http://192.168.58.250:8080/myservlet2/admin/success.jsp //登录成功后,网站的访问人数+1 //1.获取当前的访问人数 ServletContext context = this.getServletContext(); Integer count2 = (Integer) context.getAttribute("count"); //2.人数+1 if(count2 == null){ //第一个用户 count2 = 1; }else{ count2++; } //3.再存放到application作用域中 context.setAttribute("count",count2); //http://192.168.58.250:8080/myservlet2/servlet/admin/success.jsp response.sendRedirect("../admin/success.jsp"); }else{ //失败跳转回登录页面 //out.println("登录失败"); request.setAttribute("error","用户名或者密码错误"); // RequestDispatcher rd = request.getRequestDispatcher("/admin/login.jsp"); // rd.forward(request,response); //RequestDispatcher rd = request.getRequestDispatcher("http://localhost:8080/myservlet2/admin/login.jsp"); //RequestDispatcher rd = request.getRequestDispatcher("/admin/login.jsp"); //http://192.168.58.250:8080/myservlet2/servlet/admin/login.jsp RequestDispatcher rd = request.getRequestDispatcher("../admin/login.jsp"); rd.forward(request,response); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值