¶ÔÑéÊÕ²âÊÔ¡¢Gherkin¼°Spec FlowµÄ½éÉÜ
ÑéÊÕ²âÊÔ»ò¹¦ÄܲâÊÔÊÇÑé֤ϵͳÊÇ·ñÂú×ãÐèÇóµÄÒ»ÖÖ²âÊÔ¡£ÕâЩ²âÊÔ×÷ΪºÚºÐ²âÊÔµÄÒ»ÖÖ£¬ÓëÆäÄÚ²¿¾ßÌåÖ´ÐÐÎ޹ء£ÑéÊÕ²âÊÔÖ»ÊÇÓÃÀ´Ñé֤ϵͳÊÇ·ñ·ûºÏijһÐèÇó¡£
ÏÖÔÚÎÒÃÇÒ»Æð¿´ÏÂÃæÕâ¸ö¹ØÓÚÍøÒ³µÇ¼¹¦ÄܵÄÐèÇó£º
Feature: Login In order to access my account As a user of the website I want to log into the website
Scenario: Logging in with valid credentials Given I am at the login page When I fill in the following form | field | value | | Username | xtrumanx | | Password | P@55w0Rd | And I click the login button Then I should be at the home page
|
Æä¿É¶ÁÐԷdz£Ç¿£¬Êǰɣ¿ÒÔÉϵÄÏêϸÐèÇóÊÇͨ¹ýGherkinÓïÑÔÀ´ÃèÊöµÄ¡£GherkinÊÇÒ»ÖÖÁìÓòÌØ¶¨ÓïÑÔ£¬ËüÔÊÐíÎÒÃÇÔÚ²»½âÊ;ßÌåÖ´ÐÐϸ½ÚµÄÇé¿öÏ£¬ÏêϸÃèÊöÓ¦ÓÃÓ¦¸ÃÈçºÎÖ´ÐС£ÒÔÉÏÏêϸÐèÇóµÄ´ó²¿·ÖÄÚÈÝÊÇÓÉ×ÔÓÉÎÄ×Ö×é³É£»Ö»Óм¸¸öÌØ¶¨µÄGherkin¹Ø¼ü×Ö£ºFeature¡¢Scenario¡¢Given¡¢When¡¢AndºÍThen£¬ÆäËûµÄ¶¼ÊÇ×ÔÓÉÎÄ×Ö£¬²¢ÇÒÖ÷Òª¼Ç¼Á˹¦ÄÜÌØÐÔÊÇÈçºÎ±»Ê¹Óõġ£
GherkinÊÇÒ»ÖÖ»ùÓÚÐеıà³ÌÓïÑÔ£¬³¡¾°ÖеÄÿһÐУ¨Line£©¾ÍÊÇÒ»¸ö²½Ö裨Step£©¡£¡°Logging
in with valid credentials¡±³¡¾°ÖеĵÚÒ»¸ö²½ÖèÊÇ¡°Given I am at the
login page¡±¡£¸Ã²½ÖèÐèÒªÒ»¸ö¾ßÌå²½Ö趨Ò壬ÕâÑùÎÒÃǵIJâÊÔÖ´ÐÐÕߣ¨test runner£©²ÅÄÜÖªµÀÈçºÎÈ¥Íê³É¸Ã²½Öè¡£Spec
FlowÖеIJ½Ö趨ÒåÆäʵ¾ÍÊÇÒ»¸ö´øÓбäÁ¿µÄ·½·¨,¶ø¸Ã±äÁ¿°üº¬Óиò½ÖèµÄ¾ßÌåÎı¾¡£ËùÓв½Ö趨Òå·½·¨¶¼Ðè°üº¬ÓÚÒ»¸öº¬ÓÐBindingÊôÐÔµÄÀàÖС£
[Binding] class LoginStepDefinitions { [Given("I am at the login page")] public void GivenIAmAtTheLoginPage() { // TODO } }
|
ÉÏÃæµÄÀàºÍ·½·¨Ãû¶¼ÊÇËæÒâµÄ¡£ÕæÕýÖØÒªµÄÊÇÓ¦Óõ½ÀàºÍ·½·¨ÖеıäÁ¿¡£Èç¹ûûÓÐËüÃÇ£¬Spec Flow¾ÍÎÞ·¨È·Èϲ½Ö趨Òå·½·¨ºÍ¾ßÌå²½ÖèµÄ°ó¶¨¹ØÏµ¡£
ÏÖÔÚ¾Íʣϲ½Ö趨ÒåÖ´ÐÐÁË¡£¶øÕâʱºò¾Í¸ÃWatiNºÍNunitÉϳ¡ÁË¡£
WatiNÊÇÒ»¸ö»ùÓÚä¯ÀÀÆ÷µÄ×Ô¶¯»¯¹¤¾ß¡£ÎÒÃǽ«ÀûÓÃËüÀ´´ò¿ªÒ»¸öIEʵÀý£¬ä¯ÀÀURL£¬Ìî³ä±í¸ñ£¬µã»÷°´Å¥»òÁ´½ÓµÈ¡£Óë´Ëͬʱ£¬ÎÒÃǽ«ÀûÓÃNUnitÀ´¶ÏÑÔÎÒÃÇµÄÆÚÍû¡£¾¡¹ÜÈç´Ë£¬WatiNºÍNUnit²¢·ÇÊDZØÐèµÄ¡£Ò²¿ÉÒÔʹÓÃSelenium½øÐÐä¯ÀÀÆ÷×Ô¶¯»¯£»¶øÊÂʵÉÏ£¬Èκε¥Ôª²âÊÔ¿ò¼Ü¶¼¿ÉÓÃÀ´¶ÏÑÔ£¬ÉõÖÁ¿ÉÒÔʹÓÃWindows×Ô´øµÄÓ¦ÓÃ×Ô¶¯»¯¿â£¬±ÈÈ磺White£¬È»ºóΪÏàÓ¦µÄWindows
Forms»òWPFÓ¦Óñàд×Ô¶¯»¯ÑéÊÕ²âÊÔ¡£
ÏÖÔÚÎÒÃǾÍÀ´³¢ÊÔΪһ¸öÕæÊµÓ¦Óô´½¨Êµ¼ÊµÄÑéÊÕ²âÊÔ¡£Õë¶Ô±¾ÎÄ£¬ÎÒÃǽ«Ê¹ÓøÃʵÀý³ÌÐò¡£¶ÁÕß¿ÉÒÔ´ÓÕâÀïµÄrepository»ñȡһ·Ý¡£¸Ãrepository»¹°üº¬ÍêÕûµÄÑéÊÕ²âÊÔ¹¤³Ì£¬µ«ÊÇÎÒ»¹Êǽ¨Òéͨ¹ýÖ´Ðб¾ÎÄʣϵÄÄÚÈÝÀ´´´½¨×Ô¼ºµÄÑéÊÕ²âÊÔ´úÂë¡£
ÏȾöÌõ¼þ
Spec FlowΪËùÓÐÖ§³ÖµÄµÚÈý·½²âÊÔÔËÐÐÆ÷ÊÚȨÒÔÔËÐÐÕæÕý·±ÖصÄÑéÊÕ²âÊÔ¡£ÕýÈçÇ°ÃæËùÌáµÄ£¬ÎÒÃǽ«Ê¹ÓÃNUnitÖ´ÐвâÊÔºÍWatiN×Ô¶¯»¯ä¯ÀÀÆ÷¡£ÒÔϾÍÊÇÈçºÎʹÓÃWatiN×Ô¶¯»¯ä¯ÀÀÆ÷ΪWatiNÖ´Ðйȸè²éѯµÄÀý×Ó(À´×ÔWaitNÍøÕ¾)¡£
[Test] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click();
Assert.IsTrue(browser.ContainsText("WatiN"));
}
}
|
ÉÏÃæµÄ²âÊÔ´´½¨ÁËÒ»¸öÐÂIEʵÀý£¬È»ºó½«GoogleµÄURL´«¸ø¹¹ÔìÆ÷£¬È»ºóÓɹ¹ÔìÆ÷ÈÃä¯ÀÀÆ÷Ìø×ªµ½GoogleÒ³Ãæ¡£ËæºóѰÕÒÃüÃûΪ¡°q¡±µÄÎı¾¿ò¡£¸ÃÎı¾¿ò¾ÍÊÇÄãÒªÊäÈë¾ßÌå²éѯÄÚÈݵĵط½¡£ÕÒµ½¸ÃÎı¾¿òºó£¬ÊäÈë¡°WatiN¡±¡£½ô½Ó×Å£¬²éÕÒÃüÃûΪ¡°btnG¡±µÄ°´Å¥£¬È»ºóµã»÷Ëü¡£×îºó£¬ÓÉÒ»¸ö¶ÏÑÔÀ´È·¶¨Ò³ÃæÉÏ´æÔÚÓС°WatiN¡±£¨¿ÉÒÔÊÇÒ³ÃæÉÏÈκεط½£©¡£
ÒÔÉϵĴúÂëΪÎÒÃÇ¿ìËÙչʾÁËͨ¹ýWatiN×Ô¶¯»¯³£¹æÈÎÎñÊǶàôµÄ¼òµ¥£¬ÕâЩÈÎÎñ¿ÉÒÔÊÇÔÚä¯ÀÀÆ÷ÉÏÖ´ÐÐÀýÈçÌîдÎı¾¿ò¡¢µã»÷°´Å¥µÈ¶¯×÷¡£
½ÓÏÂÀ´£¬¾Í¿ÉÒÔÔÚVisual StudioÖÐΪÄãµÄÑéÊÕ²âÊÔ´´½¨ÐµÄÀà¿â¹¤³Ì¡£µ±ÄãÏÂÔØÍêNUnitºÍWatiNºó£¬Ä㽫ÐèÒªÌí¼Ó±ØÐèµÄDLLµ½ÄãµÄÑéÊÕ²âÊÔ¹¤³ÌÖС£´ÓÄãµÄNUnitÏÂÔØÖÐÌí¼ÓÒ»¸önunit.framework.dllÒýÓõ½ÄãµÄÑéÊÕ²âÊÔ¹¤³Ì¡£¶ÔÓÚWatiN£¬ÄãÔòÐèÒªÌí¼ÓÁ½¸öDLLÒýÓõ½ÄãµÄÑéÊÕ²âÊÔ¹¤³Ì£ºInterop.SHDocVw.dllºÍWatiN.Core.dll¡£

ÖµµÃÒ»ÌáµÄÊÇ£¬Äã¿ÉÒÔͨ¹ýNuGet»ñÈ¡NUnitºÍWatiNÕâÁ½¸öÏîÄ¿¡£ÕâÁ½¸ö¶¼ºÜÈÝÒ×ÔÚNuGetÖÐÕÒµ½£¬²¢ÄÜ×Ô¶¯Ìí¼Óµ½ÄãµÄ¹¤³ÌÖС£Èç¹ûÄãÒѾÔÚÄãµÄÏîÄ¿ÖÐʹÓÃNuGeÁË£¬´ó¿ÉÒÔͨ¹ýËüÏÂÔØÕâÁ½¸öÏîÄ¿¡£
´ÓÍøÕ¾ÉÏ»ñȡһ·ÝSpec Flow£¬È»ºó°²×°µ½ÄãµÄϵͳÖС£¸úNUnitºÍWatiN²»Í¬£¬ÄãÐèÒªÔÚϵͳÖа²×°Spec
Flow£¬¶ø²»ÊǼòµ¥µÄ¿½±´DllÎļþ¡£Spec Flow×Ô´øÓÐijЩ¹¤¾ß£¬Ã¿´ÎÍùÏîÄ¿ÖÐÌí¼ÓÒ»¸öÌØÐÔÎļþ£¬Ëü¶¼»á´´½¨ÏàÓ¦µÄÒþ²Ø´úÂëÎļþ¡£ÁíÍ⣬ÔÚÄã±à¼ÌØÐÔÎļþʱ£¬Ëü»¹´øÓÐһЩÓï·¨¸ßÁÁºÍÆäËûµ÷Õû¡£
³É¹¦°²×°Spec Flowºó£¬¼ì²é°²×°Ä¿Â¼£¨Ä¬ÈÏΪProgram Files£©¡£ÀïÃæÓÐÒ»¶ÑDLLÎļþ£¬µ«ÊÇÄãÖ»ÐèÒªÌí¼Ó¸ÃÒýÓõ½ÄãµÄ¹¤³ÌÖУºTechTalk.SpecFlow.dll¡£
½¨Á¢ÑéÊÕ²âÊÔ¹¤³Ì
ÔÚÎÒÃǽøÒ»²½±àдÑéÊÕ²âÊÔ֮ǰ£¬ÎÒÃÇÐèÒª½¨Á¢ÎÒÃǵÄÑéÊÕ²âÊÔ¹¤³Ì¡£ÎÒÃǽ«ÔÚ¹¤³ÌÖÐÌí¼Ó¼¸¸öÎļþ¼ÐÒÔ±ãʹÏîÄ¿¸üÓÐÌõÀí¡£
Features
ËùÓеÄ˵Ã÷¶¼·ÅÔÚÕâ¸öÎļþ¼ÐÏ¡£
StepDefinitions
ËùÓÐÇé¾°²½ÖèµÄ²½Ö趨Ò彫·ÅÔÚÕâ¸öÎļþ¼ÐÏÂ
StepHelpers

´´½¨Spec FlowÌØÐÔÎļþ
ÔÚFeaturesÎļþ¼ÐÖÐÌí¼ÓÒ»¸öеÄÃû×ÖΪLogin.featureµÄSpec
FlowÌØÐÔÎļþ¡£¸ÃÎļþ´øÓÐÕë¶Ô³¡¾°ÖÐÐÂÔöÌØÐԵĹæ¸ñ˵Ã÷¡£¿ÉÒÔ½«Æäɾ³ý£¬È»ºóÌí¼ÓÒÔÏÂÎı¾¡£
FeatureÔÚGherkinÖÐÊǹؼü×Ö¡£ËüÐèÒªÔÚÿ¸öfeatureÎļþÖгöÏÖÒ»´Î£¬½ô½Ó×ÅËüÊÇÒ»¸öðºÅºÍÆäÌØÐÔÃû¡£È»ºóÄã¿ÉÒÔͨ¹ýÈÎÒâÐеÄ×ÔÓÉÎÄ×ÖÀ´ÃèÊö¸ÃÌØÐÔ¡£ÎªÁ˱£³Ö¼òÒ×ÐÔ£¬GherkinµÄ´´½¨Õß½¨ÒéÓû§¾¡Á¿½«Îĵµ±£³Ö×î¶Ì£¬²¢×ñÑÒÔϸñʽ£º
In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal |
¸öÈË¿´À´£¬ÎÒ¸üÇãÏòÓÚÌø¹ýÉÏÊöÃèÊö£¬ÏñµÇ¼ÕâÑùÄÜ×ÔÎÒÃèÊöµÄÌØÐÔ£¬Èç¹ûÄã³¢ÊÔÒÀ¾ÝÄ³Ò»ÌØ¶¨¸ñʽ½«ÆäÃèÊöµÄ»°£¬·´¶øÈÃÈËÀ§»ó£»ÓеÄʱºòºÜÄÑÇø·Ö³öµÚÒ»ÐÐÖеġ°named
business value¡±ºÍµÚÈýÐÐÖÐÄãÓ¦ÃèÊöµÄ¡°beneficial outcome¡±Ö®¼äµÄ²»Í¬¡£ÐèÒª¼ÇסµÄÊǸò¿·ÖÊÇ×ÔÓÉÎÄ×Ö£¬Äã¿ÉÒÔÈÎÒâÃèÊö¡£½ÓÏÂÀ´¾ÍÈÃÎÒÃÇÏÈÌø¹ýÕâÒ»¶Î£¬¿ªÊ¼±àдÇé¾°¡£
Feature: Login Scenario: Logging in with valid credentials |
¸úFeatureÒ»Ñù£¬ScenarioÒ²ÊÇGherkinµÄÒ»¸ö¹Ø¼ü×Ö£¬ÆäºóÃæ½ô½Ó×ÅÒ»¸öðºÅºÍÆäÃüÃû¡£¸úFeature²»Í¬µÄÊÇ£¬scenario²»ÄÜÔÚÒ»ÐÐÖоÍÍê³É£¬ËüÐèÒªÓɸ÷¸ö²½ÖèÒ»ÆðÀ´Íê³Éscenario¡£ÎÒÃÇ¿ÉÒÔÏëÒ»Ï룺ÎÒÃÇÐèÒª×öʲôͨ¹ýÓÐЧƾ֤À´³É¹¦µÇ¼£¿
1.ÌîдµÇ¼±íµ¥
2.µã»÷µÇ¼°´Å¥
µ«ÊÇÇÒÂý£¬ÔÚÎÒÃÇÄܹ»ÌîдµÇ¼±íµ¥Ç°£¬ÎÒÃÇÐèÒª´ò¿ªÓÐ×ŵǼ±íµ¥µÄÒ³Ãæ¡£Ö®ºó£¬ÎÒÃÇÐèÒª¼ì²éÎÒÃÇÊÇ·ñ³É¹¦µÇ¼¡£¿ÉÒÔ¼ÙÉèÔڵǼºó£¬ÎÒÃǽ«±»Öض¨Ïòµ½Ö÷Ò³£¬´Ó¶øÒâζ×ÅÎÒÃÇÒѾ³É¹¦µÇ¼¡£
ÏÖÔÚÎÒÃǵÄscenarioÓÐÁËǰÌáÌõ¼þ£¨±ÈÈ磺ÎÒÃDZØÐëÔÚµÇÂ¼Ò³Ãæ£©ºÍºóÖÃÌõ¼þ£¨±ÈÈ磺ÎÒÃÇÔÚÖ÷Ò³£©¡£GherkinÖУ¬Ç°ÌáÌõ¼þÐè´Ó¹Ø¼ü×ÖGiven¿ªÊ¼£¬¶øºóÖÃÌõ¼þÐèÒªÓɹؼü×ÖWhen¿ªÊ¼¡£
Feature: Login
Scenario: Logging in with valid credentials Given I am at the 'Login' page When I fill in the following form | field | value | | Username | testuser | | Password | testpass | And I click the 'Login' button Then I should be at the 'Home' page |
ÊÇ·ñ×¢Òâµ½ËüµÄÀà±í½á¹¹£¿Spec Flow»á×Ô¶¯½«µÚÒ»ÐÐбÌ廯£¬¸ÃÐÐÔڹܵÀÏÞÖÆÐÐÖУ¬²¢ÓɹܵÀ¿ªÊ¼¡£µÚÒ»ÐÐΪ±íÍ·£¬½ô½Óן÷ÐÐÖеÄÿÁн«Ö¸´ú±íÍ·Öж¨ÒåµÄÈÎÒâÎı¾¡£±ÈÈ磬Password¾ÍÊÇfieldÔÚµÚ¶þÐеÄÄÚÈÝ£¬¶øtestuserÔòÊÇvalueÁÐÔÚµÚÒ»ÐеÄÖµ¡£
ͬʱ£¬Ò²Ó¦×¢Òâµ½ÓÉAnd¿ªÊ¼µÄ²½Öè¡£And¹Ø¼ü×Ö¿ÉÒÔʹÓÃÓÚÈÎÒ»²½ÖèÖ®ºó£¬²¢½«±»×Ô¶¯ÈÏΪÓëǰһ²½ÖèÊôÓÚͬһÀàÐÍ¡£Èç¹ûAnd²½Öè½ô½Ó×ÅGiven²½Ö裬
ÄÇô¸Ã²½ÖèÒ²±»ÈÏΪÊÇÒ»¸öGiven²½Öè¡£ÔÚÉÏÊöÀý×ÓÖУ¬¸ÃAnd²½Öè±»ÈÏΪÊÇWhen²½Öè¡£When²½Öè²»ÓÃ×öpre»òpostÌõ¼þ£¬µ«ÊÇscenarioÐèÒªÕâÒ»²¿·ÖÓÃÓÚ½øÒ»²½Ö´ÐС£
´´½¨µÚÒ»¸ö²½Ö趨Òå
ÏÖÔÚÎÒÃÇÍê³ÉÁ˶ÔLoginÌØÐԵ͍Ò壬µ«ÊÇÎÒÃǵÄtest runner»¹²»ÖªµÀÈçºÎÖ´ÐиÃÌØÐÔÇé¾°ÖеÄÿһ¸ö²½Öè¡£ÎÒÃÇÐèҪΪLoginÌØÐÔÇé¾°ÖеÄ4¸ö²½ÖèÒ»Ò»¶¨Ò塣ΪÁËÍê³ÉÕâ¸ö£¬ÎÒÃǽ«ÔÚStepĿ¼Öд´½¨Ò»¸öÀ࣬²¢½«ÆäÃüÃûΪLoginSteps¡£ÎªÁËÈÃSpec
FlowÖªµÀ¸ÃÀຬÓв½Ö趨Ò壬ÎÒÃǸø¸ÃÀà°ó¶¨ÏÂBindingÊôÐÔ¡£¸ÃBindingÊôÐÔÊôÓÚTechTalk.SpecFlowÃüÃû¿Õ¼äµÄÒ»²¿·Ö¡£
using TechTalk.SpecFlow;
[Binding] class LoginSteps { }
|
½ÓÏÂÀ´£¬ÎÒÃÇÐèÒª¸øÃ¿¸ö²½Öè´´½¨Ò»¸ö·½·¨¡£¸Ã·½·¨½«¸æËßSpec FlowÈçºÎÖ´ÐÐÿһ²½Öè¡£ÏÖÔÚÎÒÃǾÍֻΪµÚÒ»¸ö²½Öè½øÐоßÌå²½Ö趨Ò壺¡°Given
I am at the ¡®Login¡¯page¡±¡£
[Given("I am at the 'Login' page") public void GivenIAmAtTheLoginPage() { // TODO }
|
Çë×¢Òâµ½¸Ã·½·¨´øÓÐÒ»¸öÊôÐÔ¡£¸ÃÊôÐÔ½«¸æËßSpec Flow´Ë·½·¨ËùÖ¸´úµÄ²½Ö裬ÿ¸ö²½Öè¶¼ÓÐÏàÓ¦µÄÊôÐÔ¡£ÈκÎÒ»¸öfeatureÎļþÖеÄÈÎÒâÒ»¸öÒԹؼü×Ö¡°Given¡±¿ªÊ¼µÄ²½Ö裬½ô½Ó×ŵÄÎı¾ÊÇ¡°I
am at the ¡®Login¡¯page¡±µÄ¶¼½«»áÓë¸Ã·½·¨Åä¶Ô¡£
ÏÖÔÚÎÒÃÇÐèҪΪ¸Ã²½Ö趨Òå±àд¾ßÌåÖ´ÐС£ÎÒÃÇÐèÒª¸æËßWatiNÆô¶¯ä¯ÀÀÆ÷£¬È»ºó·ÃÎʳÌÐòµÇÂ¼Ò³Ãæ¡£µ«ÊÇÔÚÕâ֮ǰÎÒÃÇÐèÒª´´½¨Ò»¸öä¯ÀÀÆ÷ʵÀý¡£ÎÒÃÇҲҪȷ±£scenarioÖÐÊ£ÓಽÖèÒ²½«Ê¹ÓøÃä¯ÀÀÆ÷ʵÀý¡£ÎªÁ˱£Ö¤scenarioÖÐËùÓв½ÖèʹÓõÄÊÇͬһ¸öä¯ÀÀÆ÷ʵÀý£¬ÎÒÃÇÐèҪΪä¯ÀÀÆ÷¶ÔÏó´´½¨Ò»¸öʵÀý£¬²¢½«Æä±£´æÓÚScenarioContext×ÖµäÖС£ScenarioContext×Öµä¿ÉÒÔÓÃÓÚ±£´æScenarioÖ´Ðйý³ÌÖеÄÊý¾Ý¡£ÎÒÃÇ»¹Òª´´½¨Ò»¸öÃûΪWebBrowserµÄhelperÀ࣬¸ÃÀཫ±£´æscenarioÖ´ÐÐʱµÄä¯ÀÀÆ÷ʵÀý¡£
using TechTalk.SpecFlow; using WatiN.Core;
static class WebBrowser { public static IE Current { get { if(!ScenarioContext.Current.ContainsKey("browser")) ScenarioContext.Current["browser"] = new IE();
return ScenarioContext.Current["browser"] as IE; } } }
|
ÒÔÉϵÄHelperÀຬÓÐCurrentÊôÐÔ£¬Ëü½«»ñÈ¡ÏÖÓÐä¯ÀÀÆ÷ÓÃÓÚĿǰÕýÔÚÖ´ÐеÄscenario¡£Èç¹ûËüûÔÚScenarioContext×ÖµäÖÐÕÒµ½ä¯ÀÀÆ÷ʵÀý£¬Ëü½«´´½¨Ò»¸öÐÂä¯ÀÀÆ÷ʵÀý£¬È»ºó½«ÆäÌí¼Óµ½×ÖµäÖС£ÕâÑù´æÔÚÓÚ×ÖµäÖеÄä¯ÀÀÆ÷ʵÀýÓÖ»ØÀ´ÁË¡£
×îºó£¬ÎÒÃÇÖÕÓÚ¿ÉÒÔ»ØÈ¥ÊµÏÖÎÒÃǵIJ½Ö趨ÒåÁË¡£ÔÚʾÀýÓ¦ÓÃÖУ¬µÇÂ¼Ò³Ãæ´æÔÚÓÚhttp://localhost:9876/authentication/login¡£ÎÒÃǽ«Í¨¹ýÈõ±Ç°Scenarioä¯ÀÀÆ÷ʵÀý¶¨Î»µ½¸ÃURLÒÔÖ´ÐÐÎÒÃǵIJ½Ö趨Òå¡£ÎÒÃÇ¿ÉÒÔͨ¹ýä¯ÀÀÆ÷ʵÀý»Øµ½Ö÷Ò³£¬È»ºóµã»÷µÇ¼Á´½ÓÀ´Ö´ÐÐÎÒÃǵIJ½Ö趨Ò壬¶øÕâÔÚÏÖÔÚ¿´À´ÊÇ×î¼òµ¥µÄ¡£È»ºó£¬ÎÒÃǽ«Öع¹ÎÒÃǵIJ½Ö趨Ò壬ÕâÑùÎÒÃÇÖ»ÒªÓÐÒ»¸ö²½Ö趨Òå¾ÍÄܽâ¾öÓ¦ÓÃÖÐËùÓÐÒ³ÃæÌø×ªÎÊÌâ¡£
[Given("I am at the 'Login' page") public void GivenIAmAtTheLoginPage() { // Make sure to add the namespace the WebBrowser class is inside WebBrowser.Current.GoTo("http://localhost:9876/authentication/login"); }
|
½áβ
ÏÖÔÚÎÒÃÇÒѾΪ³¢ÊÔÖ´ÐÐÑéÊÕ²âÊÔ×öºÃ×¼±¸ÁË¡£ËäÈ»ÎÒÃÇ»¹Ã»Íê³ÉËùÓеIJ½Ö趨Ò壬µ«ÊÇÎÒÃÇÏëÔÚ½øÒ»²½ÉîÈëǰ£¬È·±£¸÷·½Ãæ¶¼ÒѾÕýÈ·ÏνÓÉÏ¡£ÔÚÖ´ÐвâÊÔ֮ǰ£¬ÎÒÃÇÒª±£Ö¤ËùÓмӵ½¹¤³ÌÖеÄÒýÓö¼ÒÑÉèÖóÉÁËCopy
Local¡£Èç¹ûÊÇͨ¹ýNuGetÌí¼ÓµÄWatiN£¬Interop.SHDocVw DLLÔòĬÈϽ«ËüµÄEmbed
InteropÊôÐÔÉèÖóÉTrue¡£Õâʱ£¬ÐèҪȷ±£½«Embed InteropÊôÐÔÉèÖóÉfalse£¬ÕâÑù²ÅÄܽ«ËüµÄCopy
LocalÊôÐÔÉèÖóÉTrue¡£
ͬʱ£¬ÎÒÃÇÒ²ÐèÒªÔÚµ¥Ï̵߳ÄApartment StateÖÐÔËÐÐNUnit£¬²»È»¾Í²»ÄÜ×Ô¶¯»¯IEä¯ÀÀÆ÷¡£Ö®ËùÒÔÑ¡ÔñIE£¬¶ø·ÇFirefoxÊÇÒòΪFirefox³ÖÐø¸üÐÂËüµÄÖ÷Òª°æ±¾£¬¶øÕâ»á²»¶ÏÆÆ»µWatiNÓëFirefoxÖ®¼äµÄÏνӡ£
ÉèÖÃNUnitµÄApartment StateÐèҪʹÓõ½ÅäÖÃÎļþ¡£Íù¹¤³ÌÖÐÌí¼ÓÒ»¸öapp.configÎļþ£¬²¢Ìí¼ÓÒÔÏÂÅäÖá£
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="NUnit"> <section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/> </sectionGroup> </configSections> <NUnit> <TestRunner> <!-- Valid values are STA,MTA. Others ignored. --> <add key="ApartmentState" value="STA" /> </TestRunner> </NUnit> </configuration>
|
Ö´ÐÐÑéÊÕ²âÊÔLogin.featureÎļþ½«ÓиöÃû×ÖΪLogin.feature.cs£¨»òÕß.vb£©µÄ´úÂëÒþ²ØÎļþ¡£¸ÃÎļþÊǺ¬ÓÐTestFixtureÊôÐÔµÄÀ࣬ÕâÑùNUnit²ÅÄÜÖªµÀÐèÒª²âÊÔÄĸöÀà¡£Èç¹ûÄãµÄVisual
StudioÖÐÒѾ°²×°ÁËNUnit²âÊÔÔËÐвå¼þ£¬±ÈÈ磺TestDriven.Net»òReSharper£¬ÄÇôÄã¾Í¿ÉÒÔʹÓÃLogin.feature.csÎļþÀ´ÔËÐвâÊÔ¡£Èç¹ûÄãûÓÐÏàÓ¦µÄ²âÊÔ²å¼þ£¬ÄÇôÄã¿ÉÒÔ´ò¿ªNUnit×Ô´øµÄ¡¢Î»ÓÚÓ¦ÓÃĿ¼ÏµIJâÊÔÔËÐÐÆ÷£¬È»ºó½«ÆäÖ¸ÏòÓÉÑéÊÕ²âÊÔ²úÉúµÄÅäÖÃÎļþÖУ¬ÕâÑùËü½«¶¨Î»Login.feature.csÖеIJâÊÔÉèÖá£
ÔÚÖ´ÐвâÊÔǰ£¬±£Ö¤Ó¦ÓõķþÎñÆ÷´¦ÓÚÔËÐÐ״̬¡£È»ºóÔËÐгÌÐò,Ò»¸öеÄIEʵÀý½«±»´ò¿ª£¬²¢Ìø×ªµ½µÇÂ½Ò³Ãæ¡£Èç¹û×öµ½ÁËÕâЩ£¬ÄǾÍ˵Ã÷¸÷¸ö²¿·ÖÒѾ±»ÕýÈ·ÅäÖã¬ÎÒÃǾͿÉÒÔ¼ÌÐøÍê³ÉÊ£ÓàµÄ²½Ö趨Òå¡£Èç¹ûIEûÓдò¿ª£¬»òûÓÐÌø×ªµ½µÇÂ½Ò³Ãæ£¬ÄǾÍÒª½øÒ»²½µ÷ÊÔÁË¡£
Íê³ÉÊ£ÓàµÄ²½Ö趨Òå±àдʣÓಽÖ趨Ò彫»á·Ç³£¼òµ¥¡£ÄãÖ»Ðè¶ÔWatiN APIÓÐÒ»¶¨Á˽⣬ÕâÑù¾Í¿ÉÒÔÅäÖÃWatiNÒÔÌîд±íµ¥»ò²éÕÒ°´Å¥ºÍÁ´½Ó£¬È»ºó¶ÔÆäµã»÷¡£
Ê×ÏÈ£¬ÎÒÃdz¢ÊÔÇé¾°Öеĵڶþ¸ö²½Ö裺¡°When I fill in the following Form¡±¡£ÐèÒªÔÚLoginStepsÀàÖд´½¨Ò»¸ö·½·¨£¬²¢´øÓÐÒ»¸ö±äÁ¿½«Æä°ó¶¨µ½ÎÒÃÇĿǰÕýÔÚ¹¤×÷µÄ²½ÖèÉÏ¡£
[When("I fill in the following form")] public void WhenIFillInTheFollowingForm(TechTalk.SpecFlow.Table table) { // TODO }
|
×¢Òâ´«µ½¸Ã²½Ö趨ÒåÖеIJÎÊý¡£¸ÃTable¶ÔÏó°üº¬ÓÐÎÒÃÇÔÚÇé¾°ÖÐËùÃèÊöµÄÖµ¡£¶øWatiNÔÚÆäÃüÃû¿Õ¼äÖÐÒ²ÓÐÒ»¸öTableÀ࣬ΪÁ˱ÜÃâ³åÍ»£¬ÎÒ¶Ô¸ÃTable¶ÔÏó²ÉÓÃÁËÍêÈ«ÏÞ¶¨Ãû¡£¸ÃTable¶ÔÏó½«ÓÉÐÐ×é³É¡£ÐÐÖеÄÿÁпÉÒÔͨ¹ýË÷Òý»òÁÐÃûÀ´»ñÈ¡¡£ÁÐÃûÓÉÇé¾°ÖбíµÄµÚÒ»ÐÐÀ´¶¨Òå¡£
[When("I fill in the following form")] public void WhenIFillInTheFollowingForm(TechTalk.SpecFlow.Table table) { foreach(var row in table.Rows) { var textField = WebBrowser.Current.TextField(Find.ByName(row["field"]));
if(!textField.Exists) Assert.Fail("Expected to find a text field with the name of '{0}'.", row["field"]); textField.TypeText(row["value"]); } }
|
Êǵģ¬Ö»ÐèÒª5ÐУ¬ÎÒÃǾÍÍê³ÉÁËÌîд±íµ¥µÄ²½Ö趨Òå¡£ÎÒÃÇÊ×ÏÈÑ»·±íÖеÄÐУ¬È»ºóÊÔͼ²éÕÒÏàÓ¦µÄ´øÓÐÃüÃûÊôÐÔµÄfieldÎı¾£¬ÓÃÀ´Æ¥Å䵱ǰÐÐÖÐfieldÁÐÖµ¡£Èç¹ûûÓÐÆ¥Åäµ½ÏàÓ¦µÄÎı¾field£¬²âÊÔ½«»áʧ°Ü£¨µ«ÊÇ»áÓÐÏàÓ¦ÐÅÏ¢±»¼Ç¼ÏÂÀ´£©¡£µ«ÊÇÈç¹ûÎÒÃÇÕÒµ½Ò»¸öÆ¥ÅäµÄÎı¾field£¬WatiN½«»á±»Ö¸Ê¾ÊäÈëÔÚµ±Ç°ÐÐËùÕÒµ½µÄÁÐÖµ¡£
½ÓÏÂÀ´Ê£ÓàµÄ²½Ö趨ÒåÒ²»ù±¾ÀàËÆ¡£ÒÔϾÍÊÇÎÒÃÇϸö²½ÖèµÄ´úÂ룺¡°And I click the ¡®Login¡¯button¡±¡£
[When("I click the 'Login' button")] public void AndIClickTheLoginButton() { var loginButton = WebBrowser.Current.Button(Find.ByValue("Login"));
if(!loginButton.Exists) Assert.Fail("Expected to find a button with the value of 'Login'.");
loginButton.Click(); }
|
¶ø×îºóÒ»²½£¬ÎÒÃÇÔòÐèҪŪÇåÈçºÎÈ¥ÑéÖ¤ÎÒÃÇÊÇ·ñÔÚÖ÷Ò³ÉÏ¡£ÎÒÃÇ¿ÉÒÔ¼ì²âÆäÎļþ±êÌâÀ´¿´ËüÊÇ·ñÂú×ãÎÒÃǵÄÐèÒª£»»òÕß¼ì²âURLÀ´²é¿´ÆäÊÇ·ñÓëÆÚÍûµÄÖ÷Ò³URLÆ¥Åä¡£ÕâÀïÎÒÃǽ«Í¨¹ýURLÀ´¼ì²â¡£µ«ÐèÒª¼ÇסµÄÊDz½Ö趨ÒåµÄÖ´ÐлáÍêÈ«ÒÀÀµÓÚ¾ßÌåÓ¦ÓãºÈç¹ûÎÒÃÇ´óÁ¿Ê¹ÓÃAjax½øÐÐÒ³ÃæÇл»£¬URL¿ÉÄܲ»»á±»¸üУ¬´Ó¶ø¿ÉÄÜÎÞ·¨ÑéÖ¤ÎÒÃÇÊÇ·ñÔÚÕýÈ·Ò³ÃæÉÏ¡£
[Then("I should be at the 'Home' page")] public void ThenIShouldBeAtTheHomePage() { var expectedURL = "http://localhost:9876/"; var actualURL = WebBrowser.Current.Url; Assert.AreEqual(expectedURL, actualURL); }
|
ÏÖÔÚÖØÐÂÖ´ÐÐÑéÊÕ²âÊÔ£¬¶øÕâ´ÎËü½«Ö´ÐÐËùÓв½Öè¡£Èç¹û³É¹¦ÁË£¬ÄÇô¹§Ï²Ä㣬Äã³É¹¦µØÍ¨¹ýSpec Flow±àдÁ˵ÚÒ»¸öÑéÊÕ²âÊÔ¡£
ÖØ¹¹²âÊÔ´úÂëÏÖÔÚÎÒÃǵIJ½Ö趨ÒåÒѾ³É¹¦ÔËÐÐÁË£¬¿ÉÊÇÎÒÃÇ»¹ÊÇÐèҪͣÏÂÀ´»Ø¹Ëһϡ£ÎÒÃÇÒѾÓÐÁËÕë¶ÔµÇ¼°´Å¥µÄ²½Ö趨Ò壬Óкܴó¿ÉÄÜÎÒÃǵÄÓ¦ÓÃÖн«ÓдóÁ¿µÄ°´Å¥¡£ÎÒÃÇÊÇ·ñΪÑéÊÕ²âÊÔ¼¯ÖеÄÿ¸ö°´Å¥Ìṩһ¸ö²½Ö趨ÒåÄØ£¿ÕâЩ²½Ö趨ÒåÖÐΨһһ¸öÐèÒªÐ޸ĵĵط½¾ÍÊÇWatiNÔÚÄ³Ò³ÃæÉÏѰÕÒÆÚÍû°´Å¥Ëù¶ÔÓ¦µÄ¾ßÌåÎı¾£¬ÒÔϾÍÊǸöÀý×Ó£º
[When("I click the 'Login' button")] public void AndIClickTheLoginButton() { var loginButton = WebBrowser.Current.Button(Find.ByValue("Login"));
if(!loginButton.Exists) Assert.Fail("Expected to find a button with the value of 'Login'."); loginButton.Click(); } [When("I click the 'Register' button")] public void AndIClickTheRegisterButton() { var registerButton = WebBrowser.Current.Button(Find.ByValue("Register")); if(!registerButton.Exists) Assert.Fail("Expected to find a button with the value of 'Register'."); registerButton.Click(); }
|
ÐÒÔ˵ÄÊÇ£¬Spec FlowΪ¸ÃÎÊÌâÌṩÁ˽â¾ö·½°¸¡£Äã¿ÉÒÔʹÓÃÕýÔò±í´ïʽ£¬¸Ã±í´ïʽ±ØÐëÊÇ´«µÝ¸ø°ó¶¨ÊôÐÔµÄ×Ö·û´®£¬ÕâÑùÎÒÃǾͽ«²»Í¬²½Öè°ó¶¨µ½Í¬Ò»¸ö²½Ö趨Òå¡£ÈκÎÓɸÃÕýÔò±í´ïʽ²¶»ñµÄÎı¾¶¼¿ÉÒÔµ±×÷Ò»¸ö²ÎÊý´«µÝ¸ø²½Ö趨Òå¡£¾ßÌåÀý×ÓÈçÏ£º
[When("I click the '(.*)' button")] public void AndIClickAButton(string buttonText) { var button = WebBrowser.Current.Button(Find.ByValue(buttonText));
if(!button.Exists) Assert.Fail("Expected to find a button with the value of '{0}'.", buttonText); button.Click(); }
|
¸Ã²½Ö趨Òå¿ÉÒÔÊÇÒÔÏÂÈÎÒâÒ»ÖÖ£º
µã»÷¡®µÇ½¡¯°´Å¥
µã»÷¡®×¢²á¡¯°´Å¥
µã»÷¡®ÈÎÒâÎı¾¡¯°´Å¥
Äã¿ÉÒÔʹÓøü¼ÇÉÀ´ÈÃËùÓв½Ö趨Òå¿ÉÖØÓá£ÈÃËùÓеIJ½Ö趨Òå¶¼¾ßÓпÉÖØÓÃÐÔÊǸö·Ç³£ºÃµÄÏë·¨£¬ÎÒÃÇÓ¦¸Ã±ÜÃâ±àдֻÓÐÄ³Ò»ÌØ¶¨Çé¾°²Å¿ÉÒÔÓõIJ½Ö趨Òå¡£¿ÉÖØÓÃÐÔÔÊÐíÄãд³öµÄScenarioÄÜÁ¢¼´Ê¹Óã¬ÒòΪÄãÒѾӵÓÐÒ»¸ö¿ÉÒÔÖØ¸´Ê¹ÓõIJ½Ö趨Òå¡£µ«ÊÇҲû±ØÒªÔÚÐèҪǰ¾Í°Ñ²½Ö趨Òå±àд³öÀ´£¬ÔÚÐèҪʱ±àд¾ÍºÃÁË¡£´ó¶àÊýÇé¿öÏ£¬ÄãµÄ²âÊÔËùÐèÒª×öµÄ»ù±¾ÉÏÊÇͬÑùµÄÊÂÇé£ºÌø×ªµ½Ò»¸öÒ³Ãæ£¬Ìîд±íµ¥£¬µã»÷Ö®ÀàµÄ¡£Ö»ÓÐÔÚ¼«ÉÙÊýÇé¿öÏ£¬Äã²ÅÐèҪΪһ¸öÇé¾°±àÐ´ÌØÊâ²½Ö趨Ò壬±ÈÈçÑéÖ¤jQuery
UIÈÕÀúÊÇ·ñÔÚµã»÷Ò»¸öÐèÒªÈÕÆÚÖµµÄÎı¾¿òʱµ¯³ö£¬ÕâÑù²»Ñ°³£µÄÀý×Óʱºò²ÅÐèÒª¡£
¶ÔÓÚ¾ßÌåÈçºÎÖØ¹¹ÄãµÄ²½Ö趨Ò壬ÎÒ½«Æä×÷ΪÁ·Ï°Áô¸øÄ㣬ȷ±£Ã¿¸ö¶¼¾ßÓпÉÖØÓÃÐÔ¡£Ö®ºó£¬Äã¾Í¿ÉÒÔ±àдеÄÌØÐÔÎļþ£¬¶øÒòΪËùÐèµÄ²½Ö趨Òå¶¼ÒѾ׼±¸ºÃÁË£¬ËüÃǶ¼¿ÉÒÔÁ¢¿Ì¹¤×÷¡£µ±È»£¬Ä㽫ÐèÒª¸ü¶àµÄ²½Ö趨ÒåÀ´×Ô¶¯»¯²»Í¬ÐÐΪ£¨±ÈÈ磺µã»÷Ò»¸öÁ´½Ó£©£¬ÄãÒ²ÐèÒª½øÒ»²½¸ÄÉÆµ±Ç°²½Ö趨Ò壬ÒÔ±ãÔÚ²»Í¬Çé¾°ÖÐÒ²ÄÜʹÓá£Ä㽫עÒâµ½ÎÒÃÇµÄ±íµ¥Ìî³ä²½Ö趨ÒåÖд¦ÀíµÄ±íµ¥Ö»´øÓÐÎı¾¿ò¡£Èç¹ûÄãÏë¹´Ñ¡¸´Ñ¡¿ò£¬Í¨¹ývalue»òIDѰÕÒ°´Å¥£¬»òÆäËüÓë±íµ¥Ïà¹Ø²Ù×÷£¬ÄÇÄã¾ÍÐèÒªÔÚ±íµ¥Ìî³ä²½Ö趨ÒåÖÐÌí¼ÓÏàÓ¦µÄÂß¼¡£×ܽá×ÜÖ®£¬ÒÔÉϾÍÊÇʹÓÃSpec
Flow±àдÑéÊÕ²âÊÔÖ®Â᣾¡¹ÜÈç´Ë£¬Spec FlowÒÀÈ»Óиü¶àÌØÐÔÐèÒªÄã×Ô¼ºÈ¥·¢ÏÖ¡£±ÈÈ磺Spec Flow»¹Óиö¸ú×Ù»úÖÆ£¬Òò´ËÄãÄܸú×Ùij¸öÖ¸¶¨ÌØÐÔÔÚÌØ¶¨ÌØÐÔ£¬Çé¾°»ò²½ÖèǰºóÊÇÈçºÎÖ´ÐÐijЩ´úÂë¡£¶ÔÓÚÒÔÏÂÇé¿ö£¬Ëü¾Í»áÆðµ½·Ç³£´óµÄ×÷Ó㬱ÈÈ磺Èç¹ûÄãÐèÒªÔÚÍê³Éº¬Óеǽ¹¦Äܵij¡¾°ºóµÄµÇ³ö£»²âÊÔǰ׼±¸Êý¾Ý¿â£¬»òÖ»ÊǼòµ¥µØÔÚÿ¸ö²âÊÔ½áÊøºó¹Ø±Õä¯ÀÀÆ÷´°¿Ú¡£
Ëæ×ŲâÊÔ²»¶Ï±»´´½¨£¬Äã»á·¢ÏÖÍê³ÉÑéÊÕ²âÊÔ¼¯ËùÐèµÄʱ¼äÒ²»áËæÖ®Ôö¼Ó¡£µ«ÊÇÄãµÄ²âÊÔÖ´ÐеÃÔ½¿ì£¬Äã¾ÍÄÜÔ½¿ìµÃµ½»ØÀ¡£¬²¢ÕÒµ½ÎÊÌâËùÔÚ¡£ÄãÐèÒª¾¡¿ÉÄÜ¿ìµØÖ´ÐÐÄãµÄ²âÊÔ¡£·½·¨Ö®Ò»¾ÍÊǽ«ÄãµÄ²âÊÔ²¢Ðл¯£¬¶ø·Ç°´Ë³ÐòÖ´ÐУ¬ËùÓвâÊÔ½«Ò»ÆðÖ´ÐУ¬¶øÇÒ½«¸ü¿ì½áÊø¡£µ«ÊÇSpec
Flow²¢Ã»ÓÐÌṩ²¢Ðд¦Àí¹¦ÄÜ£¬Òò´ËÐèÒª´Ó±ð´¦Ïë°ì·¨¡£Èç¹ûÄãʹÓÃNUnit£¬Äã¿ÉÒԲ鿴PNUnit¿´ÆäÊÇ·ñÂú×ãÄãµÄ²¢ÐÐÐèÇó¡£
Gherkin£¬ÎÒÃÇÔø¾ÊÇʹÓÃDSLÀ´±àдÎÒÃǵÄÏêϸÐèÇó£¬Ëü±»Éè¼Æ×÷ΪÏνӼ¼ÊõÈËÔ±Óë·Ç¼¼ÊõÀûÒæÏà¹ØÕßÖ®¼äµÄÇÅÁº£¬ÒÔ±ãËûÃÇÔÚÄ³Ò»ÌØ¶¨ÌØÐÔÓ¦¸ÃÈçºÎÔËÐÐÉÏÄܹ»´ï³ÉÒ»Ö¡£ÓеÄÍŶÓÉõÖÁÓзǼ¼ÊõÀûÒæÏà¹ØÕß²ÎÓ뵽ʹÓÃGiven-When-ThenÓï¾äÀ´±àдʵ¼ÊÏêϸÐèÇ󡣿ÉÒÔÏëÏóµ½£¬Åàѵ´ó¼ÒÈçºÎ×öµ½Õâµã²¢²»Ì«ÄÑ£¬µ«ÊÇÆä½á¹ûÈ´´ó´ó²»Í¬¡£
Ò²ÓÐÒ»²¿·ÖÈ˸ü½øÒ»²½µØÊ¹ÓÃSpec Flow¡£ËûÃÇÔÚ¿ªÊ¼ËùÓÐÐÂÌØÐÔ¿ª·¢Ê±£¬Ê×ÏȾÍÊDZàдÏêϸÐèÇ󣬽Ó×ŵ¥Ôª²âÊÔ£¬×îºó²ÅÊÇÕæÕýµÄ±àÂ롣ȻºóËûÃÇͨ¹ý³£¹æµÄTDD
red-green-refactorÑ»·Í¨¹ýµ¥Ôª²âÊÔ£¬×îºóͨ¹ýÑéÊÕ²âÊÔ¡£Èç¹ûÄãÒѾÔÚʵ¼ùTDD£¬´òËãΪÏîÄ¿±àдÑéÊÕ²âÊÔ£¬ÄÇÄãÓ¦¸Ã³¢ÊÔÏÂÑéÊÕ²âÊÔÇý¶¯¿ª·¢¡£
ÎÞÂÛÄãÊÇͨ¹ýʲô;¾¶£¬Äã¶¼ÐèÈ·±£Ëü¶ÔÄã¡¢»¹ÓÐÄãµÄ¹¤³Ì¶¼ÒªÐÐÖ®ÓÐЧ¡£Èç¹ûÄ㻹´¦ÓÚ½¨Ä£×´Ì¬£¬²¢²»¶ÏÐÞ¸ÄÄãµÄÓ¦Ó㬳ÖÐø¸üÐÂÑéÊÕ²âÊÔ¿ÉÄÜ»áÔì³ÉÒ»¶¨Ê±¼äÍÏÑÓ¡£Ê²Ã´Ê±ºòÓ¦¸Ã×ÅÊÖ±àдÑéÊÕ²âÊÔÊÇÄãµÄ¾ö¶¨£¬Ö»ÓÐÄãÄܱ£Ö¤Ëü½«·¢Éú¡£
|