一.問題描述
在做自動化測試斷言驗證時,更新完使用者資訊後,拿着這個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>
再次運作測試代碼,終于成功了