一.问题描述
在做自动化测试断言验证时,更新完用户信息后,拿着这个updateUserCase信息去数据库user表查询,如果返回不为空说明更新成功。
但是测试时发现断言执行失败
10:35:06.645 [main] DEBUG com.course.model.getUpdateUserInfo - ==> Preparing: select * from user where id=? and username=? and sex=? and age=?
10:35:06.646 [main] DEBUG com.course.model.getUpdateUserInfo - ==> Parameters: 12(Integer), 小兰1(String), 1(String), 1(String)
10:35:06.647 [main] DEBUG com.course.model.getUpdateUserInfo - <== Total: 0
user is:null
java.lang.AssertionError: expected object to not be null
at org.testng.Assert.fail(Assert.java:94)
at org.testng.Assert.assertNotNull(Assert.java:423)
at org.testng.Assert.assertNotNull(Assert.java:408)
at com.course.cases.UpdateUserInfoTest.updateUserInfo(UpdateUserInfoTest.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
at org.testng.TestRunner.privateRun(TestRunner.java:756)
at org.testng.TestRunner.run(TestRunner.java:610)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
at org.testng.SuiteRunner.run(SuiteRunner.java:289)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)
at org.testng.TestNG.runSuites(TestNG.java:1133)
at org.testng.TestNG.run(TestNG.java:1104)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
二.问题解决
1.拿着这个SQL语句去sqlyog查询发现是能查询出结果的;
2.尝试在配置文件修改编码为utf-8
<!-- 2.数据库连接地址 -->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/java_auto_test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<!-- 数据库用户... -->
再次运行仍然是失败的;
3.在网上看到
用字段拼接的方式,把#号改成
用#是走了预编译,你传过去的对于数据库来说都是参数值还不是字段,你这个需要拼接sql,所以用$,底层就是jdbc的statement
尝试将下面查询语句对应的#都改成$(会有SQL注入的风险,还是更建议用#)
再次运行发现还是失败。
4.想到检查pom文件,
<!-- 5.mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<!-- 8.springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
怀疑是不是我的mybatis-spring-boot-starter版本太低了,
尝试修改版本为2.1.0,
再次运行发现还是失败
5.偶然发现第一次运行测试代码断言会执行失败,第二次及以后就会成功了,也是很神奇,但是一直没找到原因
6.修改测试代码,传入userId,不传updateUserCase对象了;
再次运行发现断言执行成功了
11:10:40.206 [main] INFO com.course.cases.UpdateUserInfoTest - 请求http:localhost/v1/updateUserInfo的结果是------>1
11:10:40.206 [main] DEBUG com.course.model.getUpdateUserInfo - ==> Preparing: select * from user where id=?
11:10:40.206 [main] DEBUG com.course.model.getUpdateUserInfo - ==> Parameters: 12(Integer)
11:10:40.207 [main] DEBUG com.course.model.getUpdateUserInfo - <== Total: 1
user is:User(id=12, userName=小兰3, password=111, age=13, sex=1, permission=1, isDelete=0)
db is:User(id=12, userName=小兰3, password=111, age=13, sex=1, permission=1, isDelete=0)
11:10:40.207 [main] INFO com.course.cases.UpdateUserInfoTest - ********updateUserInfo api 验证通过*********
但是结果不太对…显示的是修改之前的结果,这也就解释了为什么前面根据修改之后的结果去查询user得到的是空;
7.在服务端增加log发现服务端能查询出正确的更新后的结果
2021-06-11 11:22:55.417 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 调用SQL更新用户信息成功
2021-06-11 11:22:55.417 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 调用SQL更新的用户是:12
2021-06-11 11:22:55.418 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 更新后查到的user是:User(i
d=12, userName=小兰6, password=111, age=16, sex=1, permission=14, isDelete=0)
但是对测试代码debug发现还是旧值
8.检查代码发现我的sqlSession定义在TestConfig里且为静态的,而且被所有的测试方法公用;
SQLSession本身不是线程安全的,共用一个SQLsession的话,导致了事务问题
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。
如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
修改为:在每次执行查询的时候新建一个SQLsession
User resFromDatabase = MybatisUtils.openSession().selectOne(updateCase.getExpected(),userId);
9.再次运行,测试代码已能获得最新的记录信息
2021-06-11 11:33:35.345 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 调用SQL更新用户信息成功
2021-06-11 11:33:35.345 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 调用SQL更新的用户是:12
2021-06-11 11:33:35.346 [http-nio-8080-exec-9] INFO com.course.controller.UserManger - 更新后查到的user是:User(i
d=12, userName=小兰8, password=111, age=18, sex=1, permission=14, isDelete=0)
10.再把测试代码改回用updateCase查询,更新后的记录;
User resFromDatabase = MybatisUtils.openSession().selectOne(updateCase.getExpected(),updateCase);
<!-- 获取更新/删除后的用户信息-->
<select id="getUpdateUserInfo" parameterType="com.course.model.UpdateUserInfoCase" resultMap="UserMap">
select * from user
<trim prefix="where" prefixOverrides="and">
<if test="userId!=null and userId!=''">
and id=#{userId}
</if>
<if test="userName!=null and userName!=''">
and username=#{userName}
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="age!=null and age!=''">
and age=#{age}
</if>
<if test="permission!=null and permission!=''">
and permission=#{permission}
</if>
<if test="isDelete!=null and isDelete!=''">
and is_delete=#{isDelete}
</if>
</trim>
</select>
再次运行测试代码,终于成功了