Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CAST 2013: Software Craftsmanship for Testers

CAST 2013: Software Craftsmanship for Testers

Testers can learn a great deal from the software craftsmanship movement. This talk, given with Matt Barcomb at CAST 2013, shows some great examples of things testers can learn from developers--and what devs can learn from testers.

Jim Holmes

August 27, 2013
Tweet

More Decks by Jim Holmes

Other Decks in Programming

Transcript

  1. WHAT’S SOFTWARE CRAFTSMANSHIP? Grew out of the programming community Evolved

    after YEARS of PAINFULLY learned lessons Movement is still evolving
  2. HOW DO WE CARE? A Tester who works alone, but

    wants to improve During code reviews During (tester-coder) pairing Tester as a Navigator
  3. WHAT’S A CODE SMELL? Code Smell is when your Spidey

    Sense lights up Points to code or tests which will likely cause you pain later http://en.wikipedia.org/wiki/Code_smell First used in Fowler’s classic Refactoring book Automated tests are code!
  4. SMELLS AREN’T JUST FOR CODE Some smells can also apply

    to non-automated testing: Multiple test scenarios conflated in one test case Overly complex charters with too many concerns Test desires that are too ambiguous
  5. SMELL: READABILITY If you can’t read it, how will you

    know if it’s a good test? How will you remember what it does next month? More time is spent reading code than writing it!
  6. [Test] public void one_method_to_rule_them_all() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); IWebDriver browser = new FirefoxDriver(exe, profile); WebDriverWait w = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); //browser.Navigate().GoToUrl("http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/ CascadingDropDown.aspx"); w.Until(ExpectedConditions.ElementExists(By.XPath("id('ctl00_SampleContent_DropDownList1')/ option[text()='Acura']"))); var sl =browser.FindElement(By.Id("ctl00_SampleContent_DropDownList1")); var l = new SelectElement(sl); l.SelectByText("Acura"); w.Until(ExpectedConditions.ElementExists(By.XPath("id('ctl00_SampleContent_DropDownList2')/ option[text()='Integra']"))); sl = browser.FindElement(By.Id("ctl00_SampleContent_DropDownList2")); l = new SelectElement(sl); l.SelectByText("Integra"); w.Until(ExpectedConditions.ElementExists(By.XPath("id('ctl00_SampleContent_DropDownList3')/option[text()='Sea Green']"))); sl = browser.FindElement(By.Id("ctl00_SampleContent_DropDownList3")); l = new SelectElement(sl); l.SelectByText("Sea Green"); w.Until(ExpectedConditions.ElementExists(By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } BEFORE
  7. [Test] public void a_bit_easier_to_read() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); IWebDriver browser = new FirefoxDriver(exe, profile); WebDriverWait wait = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList1')/option[text()='Acura']"))); var selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList1")); var optionsList = new SelectElement(selectionList); optionsList.SelectByText("Acura"); wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList2')/option[text()='Integra']"))); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList2")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Integra"); wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList3')/option[text()='Sea Green']"))); … // CLOSING ITEMS ELIDED AFTER Line breaks, FTW Whitespace! Group common things together Lose Comments
  8. SMELL: DUPLICATION Don’t Repeat Yourself (DRY) Duplication kills maintainability! Avoid

    CPDD Cut and Paste Driven Development The Fix: Centralize common actions or declarations
  9. [Test] public void a_bit_easier_to_read() { … // OPENING ITEMS ELIDED

    wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList1')/option[text()='Acura']"))); var selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList1")); var optionsList = new SelectElement(selectionList); optionsList.SelectByText("Acura"); wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList2')/option[text()='Integra']"))); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList2")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Integra"); wait.Until( ExpectedConditions.ElementExists( By.XPath("id('ctl00_SampleContent_DropDownList3')/option[text()='Sea Green']"))); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList3")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); } BEFORE
  10. [Test] public void a_bit_easier_to_read() { … // OPENING ITEMS ELIDED

    wait_on_menu_item(1, "Acura"); var selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList1")); var optionsList = new SelectElement(selectionList); optionsList.SelectByText("Acura"); wait_on_menu_item(2, "Integra"); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList2")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Integra"); wait_on_menu_item(3, "Sea Green"); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList3")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } AFTER
  11. BEFORE [Test] public void a_bit_easier_to_read() { … // OPENING ITEMS

    ELIDED wait_on_menu_item(1, "Acura"); var selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList1")); var optionsList = new SelectElement(selectionList); optionsList.SelectByText("Acura"); wait_on_menu_item(2, "Integra"); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList2")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Integra"); wait_on_menu_item(3, "Sea Green"); selectionList = browser.FindElement( By.Id("ctl00_SampleContent_DropDownList3")); optionsList = new SelectElement(selectionList); optionsList.SelectByText("Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); }
  12. [Test] public void selects_moved_to_single_method() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); browser = new FirefoxDriver(exe, profile); wait = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); wait_on_menu_item(1, "Acura"); select_menu_item(1, "Acura"); wait_on_menu_item(2, "Integra"); select_menu_item(2, "Integra"); wait_on_menu_item(3, "Sea Green"); select_menu_item(3, "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } AFTER
  13. SMELL: READABILITY Code is read 5,956 1 times more than

    it’s edited Readability is an ongoing, non-stop process (And what is clear today likely won’t be in six months. Or tomorrow.) [1] This stat is made up
  14. [Test] public void selects_moved_to_single_method() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); browser = new FirefoxDriver(exe, profile); wait = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); wait_on_menu_item(1, "Acura"); select_menu_item(1, "Acura"); wait_on_menu_item(2, "Integra"); select_menu_item(2, "Integra"); wait_on_menu_item(3, "Sea Green"); select_menu_item(3, "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } BEFORE
  15. [Test] public void selects_moved_to_single_method() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); browser = new FirefoxDriver(exe, profile); wait = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); select_menu_item(1, "Acura"); select_menu_item(2, "Integra"); select_menu_item(3, "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } AFTER
  16. [Test] public void selects_moved_to_single_method() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); browser = new FirefoxDriver(exe, profile); wait = new WebDriverWait(browser,TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); select_menu_item(1, "Acura"); select_menu_item(2, "Integra"); select_menu_item(3, "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); browser.Quit(); } private void select_menu_item(int listNum, string item) BEFORE
  17. [TestFixtureSetUp] public void run_once_before_anything() { var profile = new FirefoxProfile();

    var exe = new FirefoxBinary(@"D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); browser = new FirefoxDriver(exe, profile); wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10)); browser.Navigate().GoToUrl("http://localhost/AJAXDemos/CascadingDropDown/CascadingDropDown.aspx"); } [TestFixtureTearDown] public void run_once_after_everything_else() { browser.Quit(); } [Test] public void move_setup_out() { select_menu_item(1, "Acura"); select_menu_item(2, "Integra"); select_menu_item(3, "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); } AFTER
  18. [Test] public void move_setup_out() { select_menu_item(1, "Acura"); select_menu_item(2, "Integra"); select_menu_item(3,

    "Sea Green"); wait.Until( ExpectedConditions.ElementExists( By.XPath("//span[@id='ctl00_SampleContent_Label1' and text()='You have chosen a Sea Green Acura Integra. Nice car!']"))); } BEFORE
  19. SMELL: NOT ENOUGH ABSTRACTION Abstraction moves implementation to another area

    A test shouldn’t know how a page works Also helps cut duplication
  20. ABSTRACTION PHILOSOPHY “All problems in computer science can be solved

    by another layer of indirection (abstraction) -- except for the problem of too many layers of indirection (abstraction). “ -- David Wheeler
  21. BEFORE [Test] public void move_setup_out() { select_menu_item(1, "Acura"); select_menu_item(2, "Integra");

    select_menu_item(3, "Sea Green"); assert_correct_message_appears(); } private  void  assert_correct_message_appears() {        wait.Until(                ExpectedConditions.ElementExists(                        By.XPath("//span[@id='ctl00_SampleContent_Label1'  and  "+                                "  text()='You  have  chosen  a  Sea  Green  Acura  Integra.  Nice  car!']"))); } private  void  select_menu_item(int  listNum,  string  item) {        wait_on_menu_item(listNum,  item);        selectionList  =                                        browser.FindElement(                                                By.Id("ctl00_SampleContent_DropDownList"  +  listNum));        var  optionsList  =  new  SelectElement(selectionList);        optionsList.SelectByText(item); } private  void  wait_on_menu_item(int  listNum,  string  text) {        wait.Until(                ExpectedConditions.ElementExists(                        By.XPath("id('ctl00_SampleContent_DropDownList"  +                                  listNum+  "')/option[text()='"+                                text+"']"))); }
  22.  class  CarMenu  {          IWebDriver  browser;  

           [FindsBy(How  =  How.Id,  Using  =  "ctl00_SampleContent_DropDownList1")]          IWebElement  Make  {  get;  set;  }          [FindsBy(How  =  How.Id,  Using  =  "ctl00_SampleContent_DropDownList2")]          IWebElement  Model  {  get;  set;  }          [FindsBy(How  =  How.Id,  Using  =  "ctl00_SampleContent_DropDownList3")]          IWebElement  Color  {  get;  set;  }          [FindsBy(How  =  How.Id,  Using  =  "ctl00_SampleContent_Label1")]          IWebElement  Message  {  get;  set;  }            public  CarMenu(IWebDriver  browser)                                              {  this.browser  =  browser;  }          public  string  select_a_car(Car  car)                                              {  /* ELIDED */      }          private  void  select_menu_item(int  listNum,  string  item)      {  /* ELIDED */      }          private  void  wait_on_menu_item(int  listNum,  string  text)    {  /* ELIDED */      }          private  void  extract_message(string  message)                            {  /* ELIDED */      } } [TestFixture] public  class  v8_page_objects {        // SETUP ELIDED        [Test]        public  void  page_objects()        {                Car  acura  =  new  Car("Acura",  "Integra",  "Sea  Green",  "You  have  chosen  a  Sea  Green  Acura  Integra.  Nice  car!");                CarMenu  page  =  new  CarMenu(browser);                var  actual_message  =  page.select_a_car(acura);                Assert.AreEqual(acura.Message,  actual_message);        } } AFTER
  23. TESTERS HELPING DEVS Devs often focus on happy path and

    forget stuff like “App fails when user enters 18,446,744,073,709,551,614 Ctrl-C characters in 68 concurrent browser windows simultaneously in 38 instances of Chrome, 42 FF 21.5, and IE 4.2 on a leap day at 11:59:58PM after pouring a cup of Guatemala Huehuetenango Finca Regalita roasted at full city+ over the application server. I unplugged the database server in the middle of the commit transaction too. Also there’s a french fry in the biz tier server’s disk array.”
  24. EXERCISE ! Payroll algorithm Hourly workers Straight time for <=

    40 hours Time * 1.5 for > 40 hours Salaried workers All straight time
  25. [Test] public  void  Computing_with_40_hours_at_5_rate_returns_200() {        var  wages

     =  _computer.ComputeWages(40,  5,  true);        Assert.AreEqual(200,  wages,  "40  *  5  ==  200"); } [Test] public  void  Computing_with_41_hours_at_5_rate_returns_207_50() {        var  wages  =  _computer.ComputeWages(41,  5,  true);        Assert.AreEqual(207.50,  wages); }
  26. TESTERS HELPING DEVS It’s not just about testers learning from

    devs Devs can learn better approaches to many things Sad path coverage Exception handling End user patterns
  27. WHY SMELLS MATTER Points to areas that will likely cause

    long-term pain Maintainability Flexibility for enhancements Sanity of team
  28. WHY SMELLS MATTER Identifying and fixing smells is a great

    collaboration point Testers learn cleaner testing Devs learn better coverage