国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
程序員為什么不寫單元測試
程序員為什么不寫單元測試
                       袁光東
 
筆記曾經(jīng)做過一次“程序員在項目開發(fā)中編寫單元測試的情況”的調(diào)查。調(diào)查結果:
1.       嚴格的在項目中執(zhí)行TDD 幾乎沒有
2.       為大部份業(yè)務方法編寫單元測試,并保證方法測試通過。 占16.6%
3.       偶爾編寫單元測試,一般情況下不寫。 占58.3%
4.       為了應付項目檢查而寫單元測試,但并不保證方法是否測試通過。 占8.3%
5.       從來不編寫單元測試。占16.6%
因為調(diào)查具有一定的局限性或片面性,調(diào)查結果并不十分精確。也基本能夠反映國內(nèi)程序員編寫單元測試的狀況。很少有程序員能夠比較認真的去編寫單元測試。那么到底又是什么原因呢?根據(jù)筆者參與的多個討論,主要有下面幾種原因使程序員不編寫單元測試。
1.       為了完成編碼任務,沒有足夠的時間編寫單元測試。編寫單元測試會導致不 能按時完成編碼任務,推遲項目進度。
2.       單元測試的價值不高,完全是浪費時間。
3.       業(yè)務邏輯比較簡單,不值得編寫單元測試。
4.       不知道怎么編寫單元測試。
5.       項目沒有要求,所以不編寫
6.       在項目的前期還是盡量去編寫單元測試,但是越到項目的后期,就越失控。
 
測試常常是程序員十分厭倦的一個項目活動。測試能夠為我們帶來什么?了解這些非常的重要。測試不可能保證一個程序是完全正確的,但是測試卻可以增強我們對程序的信心,測試可以讓我們相信程序做了我們期望它做的事情。測試能夠使我們盡量早的發(fā)現(xiàn)程序的bug.
一個bug被隱藏的時間的越長,修復這個bug的代價就越大。在《快速軟件開發(fā)》一書中已引用了大量的研究數(shù)據(jù)。指出:最后才修改一個bug的代價是在bug產(chǎn)生時修改它的代價的10倍。
在這里,我們需要討論的重點是單元測試。單元測試是一個方法層級上的測試,單元測試也是最細粒度的測試。用于測試一個類的每一個方法都已經(jīng)滿足了方法的功能要求。
在現(xiàn)代軟件開發(fā)過程中,不管是xp還是rup都是十分重視單元測試。已經(jīng)把單元測試作為貫穿整個開發(fā)周期的一項重要的開發(fā)活動。特別是在現(xiàn)代軟件開發(fā)過程中,有經(jīng)常集成和漸近提交的方法論??偨Y出了非常好的單元測試理論和實踐:
  在編寫代碼之前先編寫單元測試,即測試先行
  單元測試是代碼的一部份,所有的代碼必須有單元測試,并使測試通過。(像在Spring這些優(yōu)秀的開源項目中在這方面做出了非常好的例子)
  在修改代碼之前先修改單元測試,并使它測試通過。
 
在編寫代碼之前先編寫單元測試,會帶來非常多的好處:
  在編寫代碼之前先編寫單元測試,并不是編寫代碼之前需要一次性為所有的類都事先編寫單元測試。這需要有一個粒度的把握。最大的粒度應該控制在一個類級別上,最合適的粒度是控制在一個方法級別上。先為某一個方法編寫測試代碼,然后再為該方法編寫實現(xiàn)代碼,直到其測試通過后再為另一個方法編寫測試代碼,如此循環(huán)。單元測試在這里已經(jīng)是一個契約規(guī)范了,它規(guī)范了方法應該做什么,實現(xiàn)什么。測試代碼遠遠要比難以閱讀和不會及時更新的需求文檔更有價值。
 測試先行,鼓勵對需求的理解。如果沒有理解需求,你是不可能寫出測試代碼的,當然你也不可能寫出好的實現(xiàn)代碼。
  測試代碼與其它文檔相比會更有價值。當需求發(fā)生改變,實現(xiàn)代碼也相應改變。而往往需求文檔,設計文檔得不到及時更新。測試代碼相比那些過期的文檔更具有價值。
  測試先行可以編寫出最大覆蓋率的測試代碼。如果在方法的實現(xiàn)代碼編寫完后再編寫測試代碼,這時開發(fā)人員總是編寫一個正確路徑的測試代碼。它已經(jīng)很難全面的去分析其它分支邏輯。
  如果我們采用測試先行,那么就自動的完成了為所有的類都編寫測試這個實踐原則。為所有的類都編寫測試會將為你帶來非常多的好處:
  我們可以很好的使用自動化測試來測試所有的類,特別是采用日構建的系統(tǒng)。
   可以讓我們放心的為類或方法添加新的功能。我們可以很容易的修改測試代碼并驗證修改后的代碼是有用的代碼。
   可以讓我們放心的對代碼進行重構和進行設計優(yōu)化。重構和設計優(yōu)化通常會關聯(lián)到多個類及多個方法。如果我們?yōu)樗械念惗季帉懥藴y試,我們就可以在重構代碼后很輕松的進行測試我們的修改是否正確。
   為所有的類編寫測試,可以讓我們很容易的修改bug。當接到一個bug報告后,我們總是先修改測試代碼,然后修改實現(xiàn)代碼,使測試成功。這樣不會因為修改一個問題而造成新問題的產(chǎn)生。
良好的單元測試策略給我們增強了對程序的信心,減少了bug的產(chǎn)生及bug的潛伏期。降低修改bug的代價。單元測試不會是項目開發(fā)周期的某一個生命周期,它貫穿于項目的整個生命周期,是一個非常重要的日常開發(fā)活動。
 
我們已經(jīng)知道了單元測試是多么重要的。為什么程序員仍然不編寫單元測試呢?為什么程序員總是有理由拒絕編寫單元測試呢?
一、編寫單元測試,增加了工作負擔,會延緩項目進度?
   這是筆者在多次討論和調(diào)查中程序員拒絕編寫單元測試的最多理由。“為了完成編碼任務,沒有足夠的時間編寫單元測試。編寫單元測試會導致不能按時完成編碼任務,推遲項目進度”。事實上真的是這樣的嗎?軟件有著其特殊的生命周期,軟件開發(fā)也具有特殊性。
首先,我們需要提供給用戶的至少是一個能運行的產(chǎn)品。絕對不能是一堆不能運行的和充滿了異味的啞代碼。只有能夠運行的,滿足客戶需求的代碼才是真正有用的代碼。這時代碼就變成產(chǎn)品了。
很多程序員只關注編寫代碼的完成時間,而乎略了調(diào)試代碼,集成及修改和維護時間。
如果沒有單元測試,開發(fā)活動會是這樣的情景。
以一個web應用開發(fā)為例:業(yè)務代碼編寫完成->打包->發(fā)布到服務器->進行功能測試->發(fā)現(xiàn)問題->修改代碼->再打包……如此循環(huán)。
任何一個web程序員對于這種開發(fā)情景都不會感到陌生。往往不斷的打包,發(fā)布,功能測試的時間是代碼編寫的10倍以上。通過集成系統(tǒng)來發(fā)現(xiàn)程序的bug,我們往往很難一下子準確的定位bug產(chǎn)生的地方。應用服務器提供的錯誤信息對于我們來說是非常有限的。
如果為每一個類都編寫單元測試并讓每一個方法測試通過,又會是怎么樣的開發(fā)情景呢?
編寫測試代碼->編寫業(yè)務代碼->運行測試方法->修改代碼讓測試通過->所有的類都通過測試->打包->發(fā)布到服務器->進行功能測試->發(fā)現(xiàn)bug->修改測試代碼->修改業(yè)務代碼->測試通過->再打包…如此循環(huán)。
從上面的過程顯而易見,我們需要花費更多的編碼時間。因為需要為每一個業(yè)務類編寫測試代碼。但是它并不會導致我們總體需要花費更多的時間。我們只是可以非常輕松的在ide環(huán)境中運行測試方法。在代碼尚未打包發(fā)布之前我們就已經(jīng)確保了業(yè)務代碼的正確性。當我們把所有通過測試的代碼集成到應用服務器后,出現(xiàn)錯誤的機率要少得多。當集成測試后發(fā)現(xiàn)bug時,我們也總是先修改測試類。保證在集成之前所有的類都經(jīng)過測試通過。這樣功能測試的時間就成數(shù)量級的減少,所以總的花費時間要比沒有單元測試要少得多。
 另外,如果沒有單元測試,會經(jīng)常出現(xiàn)一些低級的錯誤,如拼寫錯誤,空指針異常等。就因為一個小小的拼寫錯誤而需要重新打包,發(fā)布一次。如果有單元測試,就可以避免這些低級的錯誤。
如果沒有單元測試,把代碼集成到應用服務器后再發(fā)現(xiàn)錯誤時,我們往往更多的是憑借自己的經(jīng)驗來判斷問題出在哪里。對于沒有經(jīng)驗的程序員來說只能是撞運氣了。這就像是瞎子走路一樣,兩眼一把黑。如果每個類都有單元測試,就無需要這么痛苦了。
這使得我回想起當年做網(wǎng)絡系統(tǒng)。當時的局域網(wǎng)絡都是采用環(huán)狀網(wǎng)絡,還沒有現(xiàn)在的交換機來組星形網(wǎng)絡。環(huán)狀網(wǎng)絡的傳輸網(wǎng)絡采用同軸細纜線,網(wǎng)絡中的所有節(jié)點都在一條主干線上,網(wǎng)絡的兩端都會加上一個電阻來形成一個環(huán)。
環(huán)狀網(wǎng)絡的最大的缺點就是當任意一個節(jié)點有固障時,整個網(wǎng)絡都不能連通。維護這種網(wǎng)絡是非常麻煩的。通常采用得比較多的方法就是“切香腸”法。把最后一個電阻取下來,接到第二臺電腦的網(wǎng)絡節(jié)點的末端,檢查兩條線是否能連通。連通后再把電阻取下來到第三臺電腦的網(wǎng)絡節(jié)點的末端,連上第三臺電腦。這樣來依次檢查整個網(wǎng)絡的線路。
后來發(fā)展了星形網(wǎng)絡,也是現(xiàn)在局域網(wǎng)普遍采用的。有一臺交換機,每一臺電腦連接到交換機,任意一個節(jié)點網(wǎng)絡故障不會影響到其它節(jié)點,檢查起來就非常方便了。沒有單元測試的代碼就像是環(huán)狀網(wǎng)絡,而有測試的代碼就像星形網(wǎng)絡。
 其次,有可能我們第一次編寫的代碼是沒有問題的,但是到后來需求改變而修改了其中某些類的代碼,把它發(fā)布到了應用服務器去測試,所要修改的內(nèi)容已經(jīng)測試通過了。但是因為某些類的修改導致了其它類不能正常的工作。這種bug往往隱藏得非常深,因為只要不觸動它,它就不會出現(xiàn)??赡軙绦虬l(fā)布到生產(chǎn)環(huán)境之后才會被業(yè)務人員發(fā)現(xiàn)。如果每個類都有測試代碼,我們在打包之前運行所有測試代碼,就可以很容易的發(fā)現(xiàn)因為代碼修改帶來的連帶性錯誤。
 其三,在離bug產(chǎn)生越近,修正bug就越容易;在bug產(chǎn)生越遠,修正bug的代價就越昂貴。假設我們?nèi)ゼ梢粋€星期(甚至更長時間)前編寫的代碼,當發(fā)現(xiàn)問題時,我們已經(jīng)忘掉了很多重要的實現(xiàn)細節(jié),所以修改變得困難重重。
編寫單元測試,并不會加重程序員的負擔,反而提高了程序員對程序的信心,大大的減少了重復打包,發(fā)布,糾錯誤的時間。這些花費的時間遠遠要比編寫單元測試花費的時間多幾個數(shù)量級。編寫單元測試,可以讓你更容易和更放心的去修改代碼,增加功能從而加快了項目的開發(fā)進度。
為什么我們總是要主觀的去認為編寫單元測試會延緩項目進度呢?與其痛苦的掙扎,還不如去嘗試一下好的實踐。
 
二、業(yè)務邏輯簡單,不值得編寫單元測試
  程序員是聰明的,程序員也總是自認為是聰明的。認為一些業(yè)務邏輯比較簡單的類不必要編寫單元測試。我們必須承認,需求不斷變化,我們也必須要有勇氣去接受需求變化。編寫單元測試的另一個目的就是擁抱變化,而不是拒絕變化。編寫單元測試就是提高了我們對程序的信心。在敏捷軟件開發(fā)中,代碼為集體所有,項目組的任何一個人都可以去修改任何一個代碼文件。每當我要去修改一個別人編寫的代碼時,我總是多么的希望有程序的單元測試代碼,而往往都讓我非常的失望。一般我都得花費很大的力氣去猜想原作者的原始意圖。也許你會說:“你可以去看需求文檔??!你不會去看注釋嗎?”。但實際情況是,當需求文檔完成了它的使命后,開發(fā)人員就把它扔到了一邊了,文檔總是過期的。沒有幾個項目組能夠使得需求,設計這些文檔與最新實現(xiàn)代碼保持一致。所以去看一個過期的文檔是沒有價值的。注釋也同樣,保持最新仍然是一個最大的問題,并且注釋能夠提供的信息是非常有限的。所以我最需要的就是看測試代碼了。測試代碼最能反映出方法最新的功能契約。由代碼的編寫者去寫的單元測試要比由其它人去編寫的單元測試要更完善,更準確。
很多問題恰恰就出在一些我們認為簡單的代碼中。除非是像一個JavaBean的getter和setter方法,因為這些方法可以通過IDE自動代碼生成,沒有必要為它編寫測試。
在項目開發(fā)中,我們需要經(jīng)常通過重構來優(yōu)化代碼及改進我們的設計,當我們對代碼進行重構之后,怎么能夠保證代碼仍然是正確的?那就是運行所有被修改的代碼的測試。如果測試通過,則說明我們的重構是正確。
我們不能回避代碼的維護問題。代碼維護包括修正bug和增加功能。維護工作可能會距離代碼編寫完成有很長一段時間。當需要修改一個bug而修改了代碼,或增加一個新的功能而修改了代碼時,又怎么能夠保證修改后的代碼仍然是正確的和沒有隱患的呢?也許你會說,發(fā)布到應該服務器去測試就知道了。筆者曾經(jīng)發(fā)生過因為維護而導致了更嚴重問題發(fā)生的情況。一個系統(tǒng)在生產(chǎn)環(huán)境正常運行很長時間了。某一天,業(yè)務人員要求修改某一個功能,筆者按業(yè)務的要求實現(xiàn)了要修改的功能,業(yè)務也測試了修改后的功能,然后發(fā)布到了生產(chǎn)環(huán)境。程序下發(fā)兩個星期后,報了一個非常嚴重的生產(chǎn)問題上來,以前能夠正常運行的功能突然有問題了,導致了大量的生產(chǎn)數(shù)據(jù)錯誤。這個問題是非常致命的,只能暫時停用系統(tǒng)。
最后我查明原因是,出錯的模塊與上次修改的代碼有關聯(lián),上次修改時沒有同時去修改現(xiàn)在出錯的模塊。要是我能夠在修改代碼后,運行所有的測試類,測試就肯定會報告不通過。也就不會把隱藏有這么嚴重錯誤的程序下發(fā)到生產(chǎn)環(huán)境去。
  我們看看沒有寫單元測試是怎么進行集成的。如果某些結果與我們所期望的不一致時,我們可能會在程序中加上許多print語句,然后通過控制臺來監(jiān)視程序的運行過程。采用print語句并不能夠保證我們的程序的正確性。最好的情況是,它只能保證一條正確的路徑,不能保證其它的分支。另外當太多的print語句的信息在控制臺上,也會讓我們看不到想看到的信息,控制臺的信息是有限的。在開發(fā)測試時,把調(diào)試信息打印在控制臺還可以接受,但是在生產(chǎn)環(huán)境,如果還有調(diào)試信息出現(xiàn)在控制臺,那是絕對不可以接受的。我們經(jīng)常會忘記把調(diào)試的print語句及時的刪除掉,從而影響程序的性能。最關鍵的是,print語句不能保證程序的正確性,也不能為你節(jié)省開發(fā)的時間。只會給你帶來負面的影響。
 
三、不知道怎么編寫單元測試
  如果你相信單元測試的價值,那么去學習如何編寫單元測試最終會讓你獲益的。
以java開發(fā)為例,junit這樣的單元測試組件是非常易于學習和使用的。其它語言也有類似的單元測試組件。要相信這將是簡單和能為你帶來價值的。但是筆者見過許多程序員編寫的單元測試完全沒有起到它應有的作用。這也與不知道怎么編寫單元測試有關。所以我們應該掌握一些編寫單元測試的基本原則:
  應該為什么編寫測試:雖然我們說為所有的代類都編寫單元測試,但是測試JavaBean的setter或getter方法無異于是自尋煩惱。編寫這樣的測試完全是浪費時間。而且還增加了維護的困難。
  學會使用斷言:斷言就是讓我們?yōu)榉椒ㄔO置一個期望值。當方法執(zhí)行結果與期望值不一致時,測試組件就會報告測試不通過。我見過一些項目的單元測試不是使用斷言,而是自己編寫一個打印(println)工具類,可以詳細的在控制臺中打印出類的詳細成員信息,及集合的詳細信息。在單元測試中使用這個打印工具類來打印輸出結果。這看起來好像非常不錯。但是不應該使用這種方式來編寫單元測試
使用打印工具類,需要程序員自已從控制臺去觀察程序的執(zhí)行結果。當輸出信息非常多時,控制臺信息是無法向上翻屏的。所以不能夠給我們提供更多的信息。所以這種方法也不能用于自動化測試。
使用打印工具類,造成了一種假像,測試報告我們的測試總是成功的!如果使用斷言,當方法的執(zhí)行結果與我們設置的期望值不一致時,則會詳細的報告測試失敗的情況。
使用打印工具來代替斷言,造成測試的不充分,只會寫出一個低測試覆蓋率的測試。我們需要一個充分的測試。
  最大化測試覆蓋率:我們除了測試一個正確的路徑外,我們還需要測試方法的每一個分支邏輯。需要編寫盡可能多的測試程序邏輯的測試。寫一個充分的測試。
  避免重復的測試代碼:測試類也是非常重要的,與應用代碼一樣。測試類包含的重復代碼越多,測試類自身出現(xiàn)的錯誤也會越多。而我們需要做的編碼工作也就越多。
  不要依賴于測試方法的執(zhí)行順序:使用Junit來進行單元測試,它不能保證測試方法按照我們的意圖的順序來執(zhí)行。當一個測試類有多個測試方法時,我們不能讓一個測試方法必須在某一個測試之后執(zhí)行才能成功。Junit不能為我們做這樣的保證,我們不能依賴于測試方法的執(zhí)行順序。
  針對接口測試:我們有“針對接口編程”的oo設計原則。同樣對于測試,我們也需要針對接口測試。也就是說在編寫單元測試時,測試對象總是使用接口,而不是使用具體類。
 
四、項目沒有要求,所以不編寫
  的確在很多項目中,團隊并沒有要求程序員為每一個類編寫單元測試。反而會要求我們編寫很多復雜的文檔。作為程序員我們需要明白:程序員是編寫單元測試的最大受益者。
這不是項目經(jīng)理的事,也不是QA的事,而是程序員自身的事。因為單元測試是程序代碼的一部份。單元測試是最好的,最有價值的文檔,它應該與代碼一起交付給客戶。
   單元測試代碼不是官僚,死板的文檔。它是生動的,是程序員最有用的文檔。單元測試能夠提高程序員對程序的信心,能夠使用養(yǎng)成良好的設計原則:“針對接口編程,而不是具體類”。因為要進行單元測試,所以我們需要讓類獨立于其依賴對象(使用Mock或stub)進行測試。這就迫使我們養(yǎng)成了良好的編程習慣。
  單元測試是改進我們設計的保證。做為一個優(yōu)秀的程序員,是會經(jīng)常優(yōu)化代碼和設計,所以經(jīng)常的進行重構。一個優(yōu)秀的程序員絕對不能容忍異味代碼。而單元測試就是我們進行重構的信心保證。
  單元測試是一個日常開發(fā)活動,它貫穿于項目的整個生命周期。做一個負責任的程序員總是為自己的代碼的質(zhì)量負責的。是否經(jīng)常改進你的設計,是否讓別人很輕松的使用和修改你的代碼。
為所有類編寫單元測試應該是一個程序員應具有的素質(zhì)。項目有沒有要求,不應當成為不編寫單元測試的理由。
五、為什么越在項目的后期,單元測試就越難以進行下去?
  在很多項目的初期,項目中的大部分程序員都能夠自覺的去編寫單元測試。隨著項目的進展,任務的加重,離交付時間越來越近,不能按時完成項目的風險越來越大,單元測試就往往成為犧牲品了。項目經(jīng)理因為進度的壓力也不重視了,程序員也因為編碼的壓力和無人看官而不再為代碼編寫單元測試了。
  筆者所有親歷的項目都著像這么糟糕的情況發(fā)生。越是在項目的后期,能堅持編寫單元測試的程序就在整個項目組中不會超過15%。為了追趕進度,絕大多數(shù)程序員都把沒有經(jīng)過任何測試的代碼提交到版本服務器,項目經(jīng)理也不再追問,照單全收。這樣做的結果就是在后期,集成花費的時間越來越多,幾個技術骨干人員只得日夜加班進行系統(tǒng)集成。好不容易集成完了之后,下發(fā)給測試人員測試時,bug的報告成數(shù)量級的增長。程序員就日以繼夜的修改bug.還有非常多的bug被隱藏更深,一直潛伏到生產(chǎn)環(huán)境去。項目中,越來越多的人對項目失去信心,每一個人都在抱怨,數(shù)不清的bug,修正了一個bug,更多的bug報告上來。
每天都在修改bug,但是每天又會報告上更多的bug。于是開始有人想逃離了,有人請假,也有人離職。當項目總算結束時,每一個的內(nèi)心都清楚,項目太爛了,還有很多的錯誤還沒被測試出來,趕快逃離這個項目組吧!一半的人病倒了,或對項目的維護失去了信心。
為什么會這樣?有沒有宣導測試的重要性呢?
在項目初期應該進行宣導單元測試的重要性。
有沒有做過相關的培訓工作?在項目啟動時,需要進行一些相關的培訓,教授團隊成員最基本的編寫單元測試的技巧。
有沒有做過相應的風險防范?越是工作資歷越深的程序員,就越會拒絕編寫單元測試,他們總是有太多的理由來拒絕編寫單元測試。這些頑固的老程序員往往負責著核心的代碼的編寫。我們知道20-80定律吧。80%的錯誤是發(fā)生在20%的代碼之中的,往往最嚴重的錯誤就發(fā)生在那些老鳥們的代碼中。有沒有在事先就做好風險防范,說服他們編寫單元測試。
有沒有做好測試相關的基礎工作。有沒有針對不同類型的程序編寫測試基類,讓編寫測試變成一項非常簡單的工作。有一些代碼是依賴于特定的環(huán)境,如EJB訪問,JNDI訪問,web應用程序依賴Servlet API等。測試這些程序是非常困難的。應該編寫一些測試基類和測試stub,讓這些程序可以脫離于特定環(huán)境就像普通程序一樣進行單元測試。讓普通程序員輕松的編寫測試代碼進行程序測試。
可以實行日構建和測試覆蓋率檢查,沒有通過測試的代碼絕不允許放到版本服務器。檢查測試的覆蓋率。
 
  在現(xiàn)代軟件開發(fā)過程中,測試不再作為一個獨立的生命周期。單元測試成為與編寫代碼同步進行的開發(fā)活動。單元測試能夠提高程序員對程序的信心,保證程序的質(zhì)量,加快軟件開發(fā)速度,使程序易于維護。不管測試先行還是測試后行,沒有單元測試那是絕對不行的。
  弱者為失敗找理由,強者為成功找方法!今天你單元測試了嗎?
本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
CSDN技術中心 開發(fā)75條(寫的不錯)
程序員怎么樣保證自己的程序沒有BUG-軟件測試頻道-CSDN
優(yōu)質(zhì)代碼的十誡
Code Review中的幾個提示
Java程序單元測試工具對比
高效程序員的40個好習慣和行為方式
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服