// API callback
related_results_labels_thumbs({"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$blogger":"http://schemas.google.com/blogger/2008","xmlns$georss":"http://www.georss.org/georss","xmlns$gd":"http://schemas.google.com/g/2005","xmlns$thr":"http://purl.org/syndication/thread/1.0","id":{"$t":"tag:blogger.com,1999:blog-7905389674158671872"},"updated":{"$t":"2025-09-18T07:02:56.138+08:00"},"category":[{"term":"Programming"},{"term":"Culture"},{"term":"Testing"},{"term":"scrum"},{"term":"Agile"},{"term":"performance tuning"},{"term":"Book"},{"term":"Code Quality"},{"term":"Qt"},{"term":"Event Storming"},{"term":"database"},{"term":"file system"},{"term":"virtual machine"}],"title":{"type":"text","$t":"VIVOTEK Digest"},"subtitle":{"type":"html","$t":"Journey of Software Development"},"link":[{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https:\/\/blog.rd.vivotek.com\/feeds\/posts\/default"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/7905389674158671872\/posts\/default\/-\/Code+Quality?alt=json-in-script\u0026max-results=8"},{"rel":"alternate","type":"text/html","href":"https:\/\/blog.rd.vivotek.com\/search\/label\/Code%20Quality"},{"rel":"hub","href":"http://pubsubhubbub.appspot.com/"}],"author":[{"name":{"$t":"diro"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/13292247339970268280"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"generator":{"version":"7.00","uri":"http://www.blogger.com","$t":"Blogger"},"openSearch$totalResults":{"$t":"2"},"openSearch$startIndex":{"$t":"1"},"openSearch$itemsPerPage":{"$t":"8"},"entry":[{"id":{"$t":"tag:blogger.com,1999:blog-7905389674158671872.post-7557013007551892611"},"published":{"$t":"2017-05-04T11:14:00.002+08:00"},"updated":{"$t":"2019-04-25T17:06:35.644+08:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Agile"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Code Quality"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Programming"}],"title":{"type":"text","$t":"Google Test Adapter"},"content":{"type":"html","$t":"平常 C++\u0026nbsp;的開發工具是 Microsoft Visual Studio，然後現在的測試框架是使用 Google Test，以前都是一邊寫，一邊手動執行 \ntest case 來驗證，沒有辦法跟 VS 內建的 Test Explorer 做整合，真的蠻原始的 =.=\n\u003Cbr \/\u003E\n最近因為 TDD 的關係，這樣子的開發環境真的太鳥了，所以認真研究了一下解決方案，發現了 Google Test Adapter 這個好東西：\n\u003Cbr \/\u003E\n\u003Ca href=\"https:\/\/github.com\/csoltenborn\/GoogleTestAdapter\"\u003Ehttps:\/\/github.com\/csoltenborn\/GoogleTestAdapter\u003C\/a\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg1ZGFJHPxNKyDr0TfjWFo9J8l1xAa8gtsm1m0pqzZ6JZEKM7yE0-U4esTHKSrBQIZZiIQwLDkAbpquBUtNkt3COrGiiagYPc6ZVxcyeXtWCTHdOpfslmrLkVNZpjtJCiPmhYJzXHA7voem\/s1600\/ut-1.gif\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" data-original-height=\"849\" data-original-width=\"1295\" height=\"417\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg1ZGFJHPxNKyDr0TfjWFo9J8l1xAa8gtsm1m0pqzZ6JZEKM7yE0-U4esTHKSrBQIZZiIQwLDkAbpquBUtNkt3COrGiiagYPc6ZVxcyeXtWCTHdOpfslmrLkVNZpjtJCiPmhYJzXHA7voem\/s640\/ut-1.gif\" width=\"640\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n很簡單，照著 github 上的說明安裝完後就可以使用。幾個要注意調整的地方：\n\u003Cbr \/\u003E\n\u003Col\u003E\n\u003Cli\u003E原本它會搜尋結尾為 test \/ tests 的執行檔來分析裡面的 test case，所以如果你的 Unit Test 程式不是\u0026nbsp;\u0026nbsp; \nxxxTest.exe 的話，請記得到 [TOOLS]\u0026nbsp;–\u0026gt; [Option]\u0026nbsp;–\u0026gt; [Google Test Adapter]\u0026nbsp;–\u0026gt; \n[General] ，裡面有一個 Regex for test discovery，我是使用 UT_[\\w]*.exe\u003C\/li\u003E\n\u003Cli\u003E在同一頁中還有一個 Working directory \u0026amp; PATH extension，如果你的執行環境不是在預設的 output \ndirectory，也請記得修改。\u003C\/li\u003E\n\u003C\/ol\u003E\n這樣子基本上就可以跑了，而且只要程式碼有重新編譯，它就會自動幫你跑一次 test case（Run Tests After Build 記得勾起來) \n但還有幾個小問題：\u003Cbr \/\u003E\n\u003Col\u003E\n\u003Cli\u003E修改 Unit Test Project 會自動跑 test case，但修改原本的 project 並不會被偵測到\u003C\/li\u003E\n\u003Cli\u003E希望可以每次存檔後，就自動 Build \u0026amp; Test ，這樣只要專注綠燈\/紅燈就好了。\u003C\/li\u003E\n\u003C\/ol\u003E\n這個問題我是參考 \u003Ca href=\"http:\/\/stackoverflow.com\/questions\/33234498\/automatically-build-after-save-for-visual-studio-2015\" target=\"_blank\" title=\"http:\/\/stackoverflow.com\/questions\/33234498\/automatically-build-after-save-for-visual-studio-2015\"\u003Ehttp:\/\/stackoverflow.com\/questions\/33234498\/automatically-build-after-save-for-visual-studio-2015\u003C\/a\u003E，用 \nVisual Commander 來解決：多加一個 Visual Commander Extension\u003Cbr \/\u003E\n\u003Cbr \/\u003E\npublic class E : VisualCommanderExt.IExtension\u003Cbr \/\u003E\n{\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; public void \nSetSite(EnvDTE80.DTE2 DTE_, Microsoft.VisualStudio.Shell.Package package)\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; \n{\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; DTE = DTE_;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; events = DTE.Events;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; \ndocumentEvents = events.DocumentEvents;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; documentEvents.DocumentSaved \n+= OnDocumentSaved;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; }\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; public void Close()\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; {\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; \ndocumentEvents.DocumentSaved -= OnDocumentSaved;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; }\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; private void \nOnDocumentSaved(EnvDTE.Document doc)\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; {\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; if(doc.Language == \n\"C\/C++\")\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; {\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; \nDTE.ExecuteCommand(\"Build.BuildSolution\");\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; \nDTE.ExecuteCommand(\"Test.RunAllTestsInSolution\");\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; }\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; }\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; private EnvDTE80.DTE2 DTE;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; private EnvDTE.Events events;\u003Cbr \/\u003E\n\u0026nbsp;\u0026nbsp;\u0026nbsp; \nprivate EnvDTE.DocumentEvents documentEvents;\u003Cbr \/\u003E\n}\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n完工，現在只要存檔，就會自動 Build，並且在 Test Explorer 看到紅燈綠燈了\u003Cbr \/\u003E\n\u003Cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003E\n\u003Ca href=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhKZ2VhhOI_B0wu9CLJbI-0BY8Rt7cpk83yyKnOnGTAvxvHNGmIESb5izJGrVhVdaHEpoKo8ykGC3PcBnLXDPaQ8iRYr1jWU9e3IPmXuIWcjuh64nWJeHVFJud4ic6dcFVXFnixX7JLFfiI\/s1600\/ut-1.gif\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003E\u003Cimg border=\"0\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhKZ2VhhOI_B0wu9CLJbI-0BY8Rt7cpk83yyKnOnGTAvxvHNGmIESb5izJGrVhVdaHEpoKo8ykGC3PcBnLXDPaQ8iRYr1jWU9e3IPmXuIWcjuh64nWJeHVFJud4ic6dcFVXFnixX7JLFfiI\/s1600\/ut-1.gif\" \/\u003E\u003C\/a\u003E\u003C\/div\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E"},"link":[{"rel":"replies","type":"application/atom+xml","href":"https:\/\/blog.rd.vivotek.com\/feeds\/7557013007551892611\/comments\/default","title":"Post Comments"},{"rel":"replies","type":"text/html","href":"https:\/\/blog.rd.vivotek.com\/2017\/05\/google-test-adapter.html#comment-form","title":"12 Comments"},{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/7905389674158671872\/posts\/default\/7557013007551892611"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/7905389674158671872\/posts\/default\/7557013007551892611"},{"rel":"alternate","type":"text/html","href":"https:\/\/blog.rd.vivotek.com\/2017\/05\/google-test-adapter.html","title":"Google Test Adapter"}],"author":[{"name":{"$t":"diro"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/13292247339970268280"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"media$thumbnail":{"xmlns$media":"http://search.yahoo.com/mrss/","url":"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEg1ZGFJHPxNKyDr0TfjWFo9J8l1xAa8gtsm1m0pqzZ6JZEKM7yE0-U4esTHKSrBQIZZiIQwLDkAbpquBUtNkt3COrGiiagYPc6ZVxcyeXtWCTHdOpfslmrLkVNZpjtJCiPmhYJzXHA7voem\/s72-c\/ut-1.gif","height":"72","width":"72"},"thr$total":{"$t":"12"}},{"id":{"$t":"tag:blogger.com,1999:blog-7905389674158671872.post-7779760863467519168"},"published":{"$t":"2015-02-04T13:39:00.001+08:00"},"updated":{"$t":"2015-02-04T13:39:14.813+08:00"},"category":[{"scheme":"http://www.blogger.com/atom/ns#","term":"Code Quality"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Culture"},{"scheme":"http://www.blogger.com/atom/ns#","term":"Programming"}],"title":{"type":"text","$t":"Code review 真的重要嗎?"},"content":{"type":"html","$t":"\u003Cimg src=\"https:\/\/encrypted-tbn1.gstatic.com\/images?q=tbn:ANd9GcRIUQ7z5JsYTkT_6tcUMYsb2-rMuVdiTJXys9VHxRN_f8m-FKfh\" \/\u003E\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp;\u0026nbsp;\u003Cimg src=\"data:image\/jpeg;base64,\/9j\/4AAQSkZJRgABAQAAAQABAAD\/2wCEAAkGBxQTEhUUExQVFRUXGBcYGRcXGBccGhgZHhgWFxkZGB0YHSggGBwnHBUZIjEhJykrLi4uGB8zODMtNygtLisBCgoKDg0OGxAQGiwkHCYsLCwsLCwsLywsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLDQsLP\/AABEIALcBEwMBIgACEQEDEQH\/xAAcAAEAAQUBAQAAAAAAAAAAAAAAAwECBAUGBwj\/xABCEAACAQMCBAMFBQUFCAMBAAABAhEAAyESMQQFIkETUWEGMnGBkQcjQlKhFBUzYrFDU5LB4SRygqLC0fDxRGOyFv\/EABcBAQEBAQAAAAAAAAAAAAAAAAABAgP\/xAAhEQEAAgEFAQADAQAAAAAAAAAAARECEiExQVFhobHwIv\/aAAwDAQACEQMRAD8A9nuXDqgDuBMdzlj8Av64p4h0atM7wNsTifln\/Kq3NZYxgSo7fFj9OketJfRIjVkwe2cDHkP170EBuldkJBLbA7gEk7d2kdqibj3nFhyOxyPnGnFX+PfkwixqYScYEhfxd8Z\/Qb0uvxAMKqEYzkT541Y\/0qCo45oLG2QBvODvGBGcZisqySZlYg\/XH\/gqHijdE6FVsYnzk\/zdhFRW7nETlEAk9zgRj8Xn3\/SqNhoFNArFfxQARBJiVxpHqNj5d\/kdqivPxGICHE+QnODLdsfHzHcNgFqmgVhPcv4gId5GceX4sz+n61Ghv9952OnSVhsYMgzHeNt80GyK1TQK17tegwGmLm3h76vu9z5VerXtRkdJIHaQOrq3393t8u9Bm6BTQKweLN4CLcMcZYgE7k7CMwBsN6rxd28D92qkR3852y4\/D9T3WpaWzdApoFarXxEzpM6gYlNMeFBG86fE+dFu8UVyqBugmADPUNYzcwYnH69hL+Jq+NsFqtajxuJmAq7tlo21GDCvsFjHfO0Zne7ei3CrJjX3C4Ex1CfxeewHeQjI1NhStdbN9bSSQ9zp1Y\/xQNQB+v12rHZuJgg5OlwCugZ0oUJ1NvOobR3xTUavjc0rX8K94s5cALC6RifxTMNv7vl5epxFvcVC9IwokHT1NKycPjGrHbG+wajV8bulY1y4\/ZcdW8TOynf3Tv57YrGF2\/MBQQGAk4JGZPvfDsO+O9aabKla27c4iYVUOBnIzO0avKs2wWg6o3MR5dpoJaUpQKUpQKUpQKUpQKUpQY1y0xO8CQNz7oyfmTj4VYZ0keIAxBbcEZO+ew2mtfe4EveuReZTKtpXVhSumDJjJViI23M1T9wnRo8THhvbyCSA2jbrxGgY8ye2B0jHHufwjKNq+SesAamwYOMhe3w+m\/aq3rF6cXQF6YkCfWemMk\/0qS1wJCuGcnWWMgkETOB1dpxtEDciaxf3M0z4zHKN1ScqVJxqAgldu2pt8RmIj1WVxVu4Z0XAMRmMHJJ28iPpUdqzemTdUiTsB\/28\/wD3UKckiybZdmJYPqbMkFGhxPUDoE5zLRE1Dd9nyY03riDU7EKSAS2qYz0gaojbHnmpNdDYXuFeAVuQ+NTdiB5LkDttUZt3dJ+9UGN8Y\/TAOe2IG+Z1q8gu+KxN5xblCg13JgRKkAiBM5knA9QZf\/5vpIFwiU8OQIlevLgMNTdclsZUeoqDJNi4S33gO64YiD2PSNxO3rPkKlNi4Z6g2Ugkke65J2XupisTieQm4zFrmGcOVCkAkJbTMPn+EP8AE3oRdw\/IipWbrlRHSCVGBAHS2FG477yWnATrw12Z1iTP4j\/JssRsDmMau\/ea3w0AAMZxr6iScEYz0ye4jatcvs2oYHWwgnaZjU7RqmQD4hBiJhdoyPs8fEZxeuCTtLbQ2kTqkldUCcR2JzUEh4O8oAW\/HuzqOo4CAwXBOSG+o23qfmNq5OpHVQFYEsYiWUz7pGwIk7TMGo35QZOloH3UE6mINt2eMtJBJ8\/P5Q3uRHSdLlm0Mo8RmgyE3MmOpJ2PvHFXRjW8s6WStq4RbVbo6QA5wSeldsH1OY94GcQajg3Wyii4dS6CWJA1QQXk6Tvmr14Nib09GuNJRsjoCzsIMzG+wPww7nINSFTdck4Jlo0kGVCltsyJJOBJIkGUaU\/C8LeVl1XZQA4gTsIyVzme\/wAZqx7HEayfHULqmIWVWTH4c4nfvmcQY15AdDKb10kzDa3kSZP4s4xiPSKjPs3LFjcLErp6xqgQ2RLYcaoDdhvJJJmk0pOIscQdQN9BIAjAgmRg6Z3284jBzWXxdl2IC3IX0gGZBx0mcCIx6zWMeSHMXDB0e8CxGi611cl\/5tPwAqJ\/Z2Ti4VzdYBVKgG54knpbf7w5meldsy0kYpxwl+ZLKTqUzJ\/uijYCx72Yqbg7NwWwC4JydUzMuzEbDEEAEfSp+HsuAQzBgI0mIMBQDqg5JaT23HlNa4ez\/SoNxjpCx7wXpZGXp1fyecnUc1rDHG95IxpmXbV2elgAQSfKTIAGJwI+mxmqNZvQIuDVImQI2zGO5qLmXJzduK4u3EgEaQzaTkk6gCJ3j4bEVEeRN0\/f3sEH3j2BHY7yScyMxBAFGmfwiuI13A2I7ZMnyiI2isnxVmJE5xI7b1oLXswApHiGSWbUAQ6sSx6W1SFBadOciZyayH5Mx\/tIzdMhTP3hDETrncfQ9t6DcC4PMdu\/ntVQa5+z7NaQgD4RrDYWC3hBAusz1fwx8JONiNvy7hjbtqhIJE7bCSTA9BMD0AoMqlKUClKUClKUClKUGDZRReeGGplUlO4iRq+YIHyrI4viBbRnaYUEmKC71lYMxMxj4T5+lQc3ZRZuFlDrpMqdm9CYMA9z2q7Xui61zFGNzcC2xUlhAJA1GJyYHerBze1iXg46TMgkagCPOCCR2BExWQnDrBECG3HniDPngVFZ5dbUghRqEdRy2BpBJ89OJqT8Vj2\/aDhyY8RZ7DuwwJUbsCSIjftNUve0PDrM3VMNpOnJBxMgbASJPmY3xU9nlVlPdtosRsI2II+hAI8qXOVWWMtbQ5JyActGo\/OBPwoIxzq1JWSGDW1KkQQX0ad+33iz8fUSPObY97UuHOR+RxbYSJE6iAAN5FSnldo7ou4O2ZGkjO+NC\/4R5VaeUWf7tfxdvzMGb6sAfiKC\/huZW3XWrCBuTiOkMQZxgHPlVeH5jZcwl225gGFdSYOAcHYnvV1jgkSdKqNUAwNwBAB9IxFX2uHRcqqqYjAAx5Y7UGI\/ObIVmDBtILQsSQAWwDHZSfUCpRx66tJDA69G2NWjxBscAjz748qsflNk720MjTsNo0x8NOPhV45dbDBgIIbVju2koCfOFMfTyFanTW1jLpSlZClKUClKUClKUClKUClKUClKUClKUClK13NeecPwwm\/eS36E5PwUZP0oNjSuPX7S+Xkx4rj18N4\/pU9j7Q+XsQP2gCfzI4HzJWBQdTSrLN5XUMjBlOQQQQR6Eb1fQKUpQRB21xp6YnVI38oq3i01IwK65BGnz9KqA2v8OiMb6p\/pFRc0dhZuFJ1BSVgSdXYR3zRGRamBIgxtMx6T3rWcVzG6twqLOpQVyNUlSrkn3YBDKBEncbSDW1XbNNNWJrpWnuc4uLE8PcMgHpliATA\/CBI7gkRI3EkS8TzN1CEWHbUpYgbr7mIjeW2Me6a2cUitasfP2NZy\/mj3GhrFy3gElvd\/DgHuer9D6wbmbBSzWyAPOQcmFAxncTWziqMgOCJFSZi+GZifWt\/erYiy5B7iY+RiDiPrVw5hcgHwiJiZJxlZ2U\/m\/Q7RWxikVLjxKy9aocyuaTNpg0EjpYidOrtvnHz9DUrca+qApI1b6HGNII+ckifSthppFLjw0z61\/wC8XifBftjIOT8Ix8f0zVn7yciRbMaWIPUZIAIEBczP6GJrZxTTS4Wp9YI49pjwmid8jExOR6z8PpUCcwueGlzSHDqp0oGkEjUc5kdhjciY7bWKaRUlrHblp+I5tdWIsMcEwJJiGge7CmQJnaREzi+5zS4Ap\/Z3OokYMkQJMiPPA862umkVGrjxpE5reInwTgnp03JKw5kEgeQEEST5SKlPNLg\/s2P8UQEufhcKhmNipnYzGPKttpppoao8adOa3dCFuHYlt4nB0yZBEjOAP\/Rjv83vBXIstIXC6XPVqddwOodAMAT1jbet5pppouqPGtfjbhV3AW2E8SdYbOkHSwOBp7nf41kcDxviasQV0gjuCUV4OMEavWsoqKRVYlWlKUClK0\/tgX\/YeJ8M6W8J4PpBmPWJig859svtLum41rg2CIpjxYBZyMHTqBAXyMSd8V5zeus7FnYsx3ZiST8ScmoxVaBSlKDfeyntXe4G5KEtbPvWiTpb1H5W9R85r6D4TiVuItxDKuoZSO4Ikf1r5er2r7Hua+JwbWSZayxA\/wBxpZf11D5UHe0pSgVQmohaGstJmIiTETvH+dQc3sB7RBfQJVtRmBpYMJgg9uxFWOdxmEiqG4AJkR5zitJa5Y0z+0uwBOCSR8GGqCexxEdgeqpf3ejW7dlnBNrwjgkToPT0hsTHrVyiI4m0tuKpNc9a5YSNP7W7DEwzSdLZ6g8ichvXyGKzOA5Zo8TVddy4ALSQwgMBmTnqPkMDHc5VtGcASSAPM01iYkTvHpWuu8vRraIXZ9LI2pmJJ0NqGqImY\/T0rAHKWEA8SSzC8qsQdfUUc6SHkBfD29TttW4jGeZHRTStTw3LnRtXis4Ll21FsDQyhVAbTEkHaMDBOa2NziFUAkiCQPmSAPqSB86z3UCWlR2b6sJUyJI+YJU\/QiKkqBSlKBXP+1\/tOOAW27WnuIz6WZSOjG+dz5DFdBUXFcMlxGS4oZGEMpEgj1oMflPNrPE2xcsXFdT5bg+TDdT6Gs2vD+I4k8m5i37O63bLRqQOCSmeh4911MwfIjzNexcl5ra4qyt6y2pG+oPdWHYigzqUpQKUpQKUpQKUpQK4f7XeZG1wOhTBvOEP+6AWb+gHzruK86+2y3\/stk+V6Pqj\/wDag8dpSlApSlAru\/sd5h4fGtaO162R\/wAS9Q\/5ddcJXU\/ZhH7zsav\/ALI+PhvFB79SlKDGXw\/EMafE05\/Npn+kx+lV46PDbVIEZ07x3jFXC6NZWDMTMGInadp9Kt49gLbFhIjImJHxonTXX7XDknUwB1PIJAzHVuPSZqq27EBQekBYM9JEvEHY7v8ACO0UvcTZBOtDMsDgnZRqOCcERvvii8TZIHRCQIMer9txs\/17zW9\/rltfS26lkg62Iw8lgBjxJMkiPf8A\/DVRw\/DaLo1LpYHWdQwNLTntjV8qo3EWgDqQwNffV\/aQYgyTqg+m+KuN+w1u9AIXS3iQpBjS09pmJ+dJ4XGrjhrrPA8DbiLigAqBLgZJA0\/MdJU9icCTMnBcHwdp1KMMKzA6lKxIGcZP34+MiZMVUcz4TKaGGh0J6GEXT0jq\/P2JnuZMSalH7M1rxmtFFXWI0kkTcBbFsnJcA4zgHtjDqnS3wp1qpQm\/q1Q0lpDKSMmMBhjyNRpy2xqKrchhDaVZcS6ODEYBKJ9BHrbxB4Wzpc24B6wQrQIiGjt\/EEY3Y+tSfve3qZlVD\/CGvUASHuNa8uxXzyK6YxnX+bpFlq\/ZtzcW7rCLdbSNBJ1nxWIwD+BiB8d8RtbfGqWK7ENoz3OjX0+fTn5GsROW2LgJ8NfxIfgJtkYPkCPgT5msheXIGDAEENq95stoNuTnPSYz6eQqZTjPF2rLpSlYCouJWUYTplTnyxvUtKD5Zbc5n18\/Wu2+yr2hHD8T4LtFq\/C52W5sh+fu\/MVrvbX2TvcHcZ2Aay7todB0iTIVh+A5wO8GNq5mg+p6Vwn2ce2w4pfAvkC+oEEkDxR5j+YRkfPzju6BSlKBSlKBSlKBXK\/aP7PftnCnS2l7RNxZ2MAyp+I7+ddVWLzRiLN0rOoW3IjedJiKD5hFVqi7VWgUpSgV2n2Scu8Xjw5B02UZ57aj0KD\/AIif+GuLr2T7F7FscLcdSDca5D5yAB0Y7DJP1oPQ6UpQQh21xp6YnVI38o3qLmlx1tOUUOwGFaYJ9YyfhUw1a+2iPWZ\/pFQc2uMtm4UnUFJECSPUCDJG4Hel1KMIcybq+4ZimuSBGrSI6ARknsJ2Iz2qO1zkswU2HXCGSOkFs5IB2xkdz2zG7QYqsVvVj5+Smns8a37Obj2gjBZIPugkKSfPT1Z8tJ8qo\/M3DMgsl4YrIBAjTbiSQZlnO2AFbeM7krTSKkZRfCtS\/GkW7dw2SS+kEKCYkMSDKz2gSBJYAxk1jcRzu5b0xwzmRrIEkkHVGYgEEDVO0iJ7b+KaazPI0vEc4ddJPDXGDa9oJGkSZHmSMAEzv6ViWua3CATw496CoVpKBXYOpKjEqFAI38pEdLppFIHP2easgAFsR9\/KrbuCGF4KrEwcEMWJiTBI8qu4fnN06QbeWe2D0uAoItF5mZI8Rh2H3bZxB30Uig1XF81dHZfDZ4VGhQZz4pbOxICKIHdhnOJOM5kyOEFpmkbjbcgSQDAxnuNS4OY2MUirMx1A0Tc9uC4VPDuVwJE4I16pJAB2WImQZ9KrxXPLq6lHDvqCgg5KEkEgYGo7RtuYxvW800ioOZ5jxrXrFxLtjWjIZXS\/S0XG0N3YjwwJWMlY3mvKPbb2YHCuXs6zYLBRqDSpKK4yRkGSJ81Ir32K8k+2TnhNxOEUkKoD3PJmPuD5AT8\/Sg82tXCrBlJVgQQQYII2IPY17b9n3tyvFqLN8heIGB2F0Ae8PJt5X5\/DxCrrV1lYMpKspBDKSCCNiCMg0H1LSvL\/AGN+05Sq2uNJDbC\/jSfLxAPdP8wx5xXptm8rqGRgykSGUggjzBG4oL6UpQKUpQK4vm32l8FZdkBe6VwfDUFZ7iWIB+VTfajzg8PwLBDD3SLakbgGS5Hl0gifUV4NQZnOL9p7zvZQ27bGQhIOmdwCO0zA7DFYdKUClKUCtp7Oc+u8HeF2yfRkPuuv5W\/yPatXSg964H7ReBe2rPeFtiMowaVPcSBB+NK8FpQfUIQ651dMRpgb+c1deUkEA6TGDExVotDWWkzERJjfePP1q68gKkEkAjcGCPn2oi62CAATJjJ8\/WsG\/bfWSG0jo\/H5E6pBUhZkbbx2rOtrAAyYEZMn5nvWo4nl63Lzan\/uW0DUCNDuVJIaCCdWIjFaxq95JjZeOHvnDXFghvdkGcwQQJHbv9akNi6T72JMQxBAhgMR1HK4M7EzmA5hy5nPS5XoKzLSDrVjEEGGA0mCDEQR2i\/dLkkteubNpCl10kmQTD9USQJxEeU1JyTQr4PEdri7LuJO3VJ05k+lX8anEaptsmnTsxjq1SZ6GwVwCIjOGnGO\/I31EjiLwXp0rrc6YUjctLDUQ2d4gyMDIs8qiy9osSG1gElzhicHUxJifMekVJm1iKRDhuJ0\/wAYa4b8K6dWyfhnT3OZ8jUP7JxcLqviQUmNImCkifDO\/wB5OPyxGa2DcAYu\/eXJuTBJ\/hyCOiIgCZ+VYbcnfbxiVDKQHDNEOjiSXkx4YAO\/U281cYjuVZ7JcLgrcXRBldMknMENOAPKDtWB+x8RrLeJ+Jz7zEMpdCoKlSE0oGEiZMHuRU\/B8uZHUysDxj0gifEcPEScAzn4etbOpPOw5p+X8XoIDorG1aQt4rk6l8QOwm3idSZiTpIxgiU8DxYBKXQp++wzl1Gq4GtnqtzISViYGPezXQUoNJw3D8YHBe7bZdYJAEdEEEe5vJB37b1XiOEvklwwDE2fu\/EYIoVka4AdGZhhOkSImK3VKDS8TwnFFYW9Eo8npBDmNGmLZwI75yd65j2s9iLvGBnLr4qhtHUTqySEPSAoghcYldXcivQaUHy5xFhrbMjqVZSQyncEdjUde6e3\/sSvGr4toBeJUYOwuD8r+vke3wrw\/iLDIzI6lWUkMpEEEdjQR10fsv7Y8RwYKI02zHSw1ac5KAkQY7TFc5Sg955B7WWuLRls31F86itu4NLATgTBDEZyAYkSDGdnzM3IUNctoMgzeKliUEPIQSQ2roiCCDjavnRTBkYI7ijsTuSfjn+tB7+guudK37V0kLpi8VJ0Xi0wif3ZAJzJERFc57We0b8Ktz\/aUe+xuBVt3WJSVtKDoCaRp0OYbYtuxmvJeHvMjakZkYTDKSpEiDBGdiRVhoMzmnNL3EObl641xj5nA9FGyj0FYdKUClKUClKUClKUClKUH08PD8Qxp8TTn82mf6TH6VJxOnS2uNMZnaKoLo1lYMxMwYidp2+VXX3AUkgkAZAEz8u9ELGnSumNMCI2jtHpFYtpALzkOJZUJTuI1DVvsZAiO3rWZaaQCJEiciD8wdqjF0aysGYmYMRO07fKgrxN8W0Z2wqgk\/AVDZ5gjFwD\/DbSxOBMaiB8BVObsos3C66l0mVkjV\/LI2B2n1qW3w6wRHvbjecAGSd8ACrtX1UacytH8YGAYOCAVLCQdjpEwcxUZ5zYAk3UAgmdQiB3p+57UQV1e7Mk5KrpBMGCdOCe4wcVJc5dbZSpUaSFWM7KZUekEyKgsTm9ksFW4rMxIAUzJAmMfCnEc2tI2lmjqCEkHSGKlwC206RMT3FXJy22CCFAIMgiQZlj55y7fU1aeU2jugO2JMYUoDExOkxO8UFBza3jMAi4dR26HFth8dRHxmrLfPLJjqKztqVgD0C5uREaSDO1Xnk9r8v5iOp8FmDsR1b6gDPmKDlFqI0YmYlo93RETtp6Y2igv4Tmlm6dNu6jmNUKwJjzx2qL992My+mA5OsMuEbQx6gMBsevar7PKLKmVQA4E5nBBEmZPuj6UblFoiCsjqOS34mDN3\/MA3oRIzQWrzuwTC3FbOk6eoKdJbqK4UaVJkwMVJa5naZNetQpJWWIGQxTv5lTHnUf7lsf3YyZO+TBUznqwxGfM1OvAW9ATQpUGYYasyWJ6pkySZ8zQUfmVoIXNxNCzLalgRAMmfMj6irE5tZO11Dt+Id4A\/UipH5fbKFNC6GMlQAATMyQO8gVCvJrIwLaxMxEjZhgHb3mx5sTuaCz9+2CyqtwOXBK6JaYIH4ZzmY8gT2rjfb7kfD8db8awQOIVHbEdaoFLI\/kwDCD6+VdoOS2QQdGRsZaRtsZkHAE7kADaqtyeyfwbgjBYSCqoQYP5UUfIUHzZxXDPbdkuKVdTBU7g1FX0Lzn2L4TiQRctwx\/tFJDjfuSZ3JgyCTO9eZ+0P2Y8VZJax\/tFv0xcHxX8Xy+lBw1Kuu2yrFWBVgYKkEEHyIORVtApSlApSlApSlApSlApSlApSlB9RC4dcaTETqxE+W81W8xAJALEDYd\/rVo16\/w6Ixvqn+kVdemDpjVGJ2n1iiLrZJAJEGNsY9MYqwXDq06TETqxEztvNX25gTExmNp7xVg16vw6I9dU\/0igpxiA22BXUCCCvmD2q60ekSCMDByR6E96renSdMau07T6xRJgTExmNp9KDUcTz1kutb\/AGe6QIhwOlpE49ew8zAxvVl3n7D\/AONd90mSABOoLHmQDMkA4AOQRUvFPxC3WKAPb6AqwBHvlmJmTkKvYANMGMwnjuLgkWANhEgmTMt70EAjaRMjIqKz7fHtptTacm4BOkCEJUElpMgSSPPFQ8Xzjw2INq4YgYBJJKlsADIkBZn3j86mtvf02ulSSB4pLQV6RJAAIY6pxIFRvcv+IQAmg6IMHGG1ydUnIUAQN5zFaiLEV7nFxVLfs7mFDQCJySFGfhJ8h51S3zpy6jwGVS2ks5iBDwcAg5tnvGVM5qN+YcSNRNiFVGO+pi4WQoVTmWwMiR5VvIpMTHIi4PiBcRXGzKGHzE1NVAKrUClKUClKUClKUClKUGs5xyDhuKEX7KP5MR1D4MMj61wHOvskBk8Lej+S7kfJlE\/UGvUqUHz1x3sPx9qdXDOwHe2VefWEJP6Vz9xCpIYEEbgggj4g19S1reb8h4fiRF+yj+RI6h8GGRQfNVKk4iyUdkO6syn4gkf5VHQKUpQKUpQKUpQKUpQfUIQ651dMRpjv5zV15SQQDBjB3irRaGstJ2iJx8Y86uvJKkSRI3GCKIutggCTJjJ8\/WrAjap1dMRpjv5zV9tYAGTAiTv86jFoa9UnaInG+8edBHzQMbNwJq1aW06TB1R0we2YrIQ43BPePP8AyrF5vaLWXAumziTcH4QCCe4gQCNxvWAOU3JYrf06mLdKEHNpbeeuGMqGkjftWoiO5VuqrFY\/AWCiKrMXIEFjufXJP9ayKyFIpSgpFVpSgUpSgUpSgUpSgUpSgUpSgUpSgUpSg+c\/bbhvD4\/iViPvWb\/F1\/8AVWkrtvte4bTzAt\/eW0b5iU\/6RXE0ClKUClKUClKUClKUH1CLS6y0DXEE94n\/AEq6+oKkMJWM\/CqUoi+0oAAAgAQB6dqjFpdZaBriJ7xP+lKUFnMrGu06zEqc7wexI7j077VOm1KUO2NxXMEtzqnGjYd3bQv61Xi+PS375jod9ielNOo\/8w+tKVqY2if7iFXWeMVmdRM2yFbHcqGgfJh9au4nilRWdjCqJPwpSsivD31calyJYfNWKn9QalpSgUpSgUpSgUpSgUpSgUpSgUpSgUpSg8l+2+x95wz+a3F+hUj\/APRrzKlKBSlKBSlKBSlKC9bROwqlKUH\/2Q==\" \/\u003E\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n很多書都在強調 code review 的重要性。在我們的軟體部門剛開始成立，並沒有 code review。這個狀況持續了幾年，一直到前年，我才開始認真思考，不做 code review 真的對嗎? 於是我們從那個時候開始逐步推動 code review。\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n在沒有 code review 的時代，有兩件事情讓我印象深刻。第一件事情已經七、八年了，有一個週末，我們因為程式在下一週初要 release，想當然爾，工作是做不完地。所以我決定週末來加班。那時候我自詡應該當規劃者，所以平常是不寫程式碼地。可是真的來不及了，所以我得跳下來自己幹。沒想到那個週末，當我打開 project 的程式碼一看，我快嚇傻了。一個函式長達一千多行，眼睛東瞄西瞄看到的是許許多多重複的程式碼。這個該怎麼改呢? 到最後就是能頭痛醫頭腳痛醫腳，可想而知，那一個週末沒有什麼好下場了。第二件事發生在三年前左右，我們一個產品內的某一個 service，在屢次的進測，總是領到最多的紅牌。最後在產品被追殺的階段，我們發現那一個 service 無法收斂了。這時候我們開始投人進去那一個 service 進行 code review。不看不會怕，看了之後才知道多可怕。那一個 service 竟然高達一萬四千多行。可怕的是，這一萬四千多行在同一個檔案。是地! 一個 service 就一個檔案。最厲害的地方是，這個 service 破了之前的一千多行記錄，有一個函式高達三千多行。最後那一個 service 只能靠硬測的方式讓他自然地收斂。然後在後面安排 refactor。Refactor 後剩下八九千行，當然也拆了不少的檔案。可以看出來原來的作品中重複的程式碼有多嚴重。我們稱這種大函式叫：\"吾碼一以貫之\"，這種一個函式殺到底的寫作方式，對於寫的人來說當然過癮，不用想太多次該怎麼擺變數，想一次就可以用 \"很久\"。但是對於看的人來說，實在是痛苦至極，看到第三頁的時候，已經不記得這個變數什麼時後宣告、什麼時候改值。諷刺的是一個函式不能太長這件事，在公司的 coding rule 當中是有提到地，為什麼還會看到這樣的事情出現？問題就在於事情沒有人去看，就容易歪掉。\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n這種事情就是這樣，開始總是最難地。前年開始推 code review 的時候，第一個遇到的難題就是人力的問題。這其實是一個弔詭的問題，本來寫 code 寫得很用力的團隊，突然要 \"插\" 一件 \"本該做的事\"，卻變成當時團隊多出來的事情。於是我只能動用手上所有的資源幫忙 review。然後因為遇到大家的時間不容易湊在一起，所以也請成員架上 Reviewboard 來協助分散式 code review。然後先是一個團隊持續地看到 review 活動，然後是另一個團隊的重要修改也開始 review。最讓人驚艷的是去今年初，已經有一個團隊將 Pre-commit review 自動化架好了 (用 Git + Gerrit + Jenkins)。只要 commit code，系統會自動驗證可否 build，然後會寄出 review request 給設定好的人。所以現在信箱一天會收到將近十封的 review request (說實在，加上 build 成功的訊息，一天信箱會被灌入 3, 40 封信，很快就會爆了 XD)。這樣的自動化，最有趣的地方在於讓你的 code 沒有被 review 過就不能夠 commit。因此現在對那個團隊而言，已經不是 optional, 而是 must。所以 review 活動就會被持續執行。而現在也會發現，因為 code review 的工作被拉出來，組長的工作裡頭自然會加入此項 task，所以比較不會像剛開始一樣覺得根本排不進去。\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n那麼到底這樣強推 review 有沒有什麼效果呢? 說句老實話，如果你期待 code review 可以大量減少 bug 數量，是比較不切實際地。但是他對於防範 non-trivial 的 bug，尤其是架構性問題、易維護問題、後續加新功能的 side effect 的防範，是有比較大的效果。另一個重點是，因為強制 code review，你可以在無形中強迫 member 去看到其他人被要求了哪些東西，下次他自己寫的時候就不容易重複同樣的錯誤。另外，自己寫的東西有人在看的時候就會比較小心一點，這些都是無形的效果。\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n現在開始有團隊在導入 scrum 了，在該團隊中更增加了 peer 的 code review。不再單純是上對下的 review。我覺得這樣做又有更進一步的效果，那就是懂 code 的人更多了，對於未來 task 的相互支援，也會變得比較有可能性了。這是我們在 code review 開始前享受不到地。\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n所以 code review 重要嗎? 非常重要。你們開始做了嗎?\u003Cbr \/\u003E\n\u003Cbr \/\u003E\n\u003Cbr \/\u003E"},"link":[{"rel":"replies","type":"application/atom+xml","href":"https:\/\/blog.rd.vivotek.com\/feeds\/7779760863467519168\/comments\/default","title":"Post Comments"},{"rel":"replies","type":"text/html","href":"https:\/\/blog.rd.vivotek.com\/2015\/02\/code-review.html#comment-form","title":"0 Comments"},{"rel":"edit","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/7905389674158671872\/posts\/default\/7779760863467519168"},{"rel":"self","type":"application/atom+xml","href":"https:\/\/www.blogger.com\/feeds\/7905389674158671872\/posts\/default\/7779760863467519168"},{"rel":"alternate","type":"text/html","href":"https:\/\/blog.rd.vivotek.com\/2015\/02\/code-review.html","title":"Code review 真的重要嗎?"}],"author":[{"name":{"$t":"perkins"},"uri":{"$t":"http:\/\/www.blogger.com\/profile\/17509020938165298852"},"email":{"$t":"noreply@blogger.com"},"gd$image":{"rel":"http://schemas.google.com/g/2005#thumbnail","width":"16","height":"16","src":"https:\/\/img1.blogblog.com\/img\/b16-rounded.gif"}}],"thr$total":{"$t":"0"}}]}});