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

Unit Testing with RUnit

Unit Testing with RUnit

using RUnit, R Studio, & Git
Presented at Kansas City R Users Group (March 7, 2015)

Kevin O'Brien

March 07, 2015
Tweet

More Decks by Kevin O'Brien

Other Decks in Programming

Transcript

  1. Context • check units of source code • write the

    test, then the function (TDD) • kata learned by repeated execution • red, green, refactor • “just enough” code to go green
  2. History 1960 - Test-case library (punch cards/fortran) 1994 - SUnit

    (Smalltalk) 1998 - JUnit (Java) 2000 - NUnit (.Net) 2008? - RUnit (R)
  3. Flavors • svUnit • Testthat • RUnit - most similar

    to unit testing for other languages
  4. RUnit Tests “checkFuncs” TEST SYNTAX testName <- function() { checkFuncs(a,

    b) } CHECK FUNCTIONS • checkEquals • checkEqualsNumeric • checkException • checkIdentical • checkTrue
  5. TDD Basic Concepts Unit Tests guide step-by-step success • function

    exists? • call returns a value? • value matches expectation?
  6. Example - convert ℃ to ℉ f = 9/5c +

    32 DON’T WRITE THE FUNCTION!
  7. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  8. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  9. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  10. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  11. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  12. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  13. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  14. Test Controller - run_tests.R # run_tests library('RUnit') testsuite.c2f <- defineTestSuite(

    name = "c2f", dirs = file.path("tests"), testFileRegexp = "^runit.+\\.[rR]$", testFuncRegexp = "^test.+", rngKind = "Mersenne-Twister", rngNormalKind = "Inversion" ) source('c2f.R') testResult <- runTestSuite(testsuite.c2f) printTextProtocol(testResult)
  15. With R Studio that looks like... c2f ├── README.md ├──

    c2f.Rproj ├── c2f.R ├── run_tests.R └── tests └── runitc2f.R
  16. TDD: Red, Green, Refactor Red - Write a test that

    will fail Green - Write code to make the test pass Refactor - Clean up
  17. Version COntrol with Git? Initial commit - required components Branch

    - new branch for each test Merge - When test works, merge to master Why? - Master always works branches are a history of progress
  18. Example - convert ℃ to ℉ f = 9/5c +

    32 DON’T WRITE THE FUNCTION!
  19. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { fTemp <- c2f(0) } RED - because there isn’t any such function FUNCTION FILE
  20. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { fTemp <- c2f(0) } GREEN - but no test! FUNCTION FILE c2f <- function(c) { return(0) }
  21. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { fTemp <- c2f(0) checkEquals(32, fTemp) } RED - 32 ≠ 0 FUNCTION FILE c2f <- function(c) { return(0) }
  22. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { fTemp <- c2f(0) checkEquals(32, fTemp) } Green - 32 = 32 FUNCTION FILE c2f <- function(c) { return(32) }
  23. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { fTemp <- c2f(0) checkEquals(32, fTemp) } Refactor - Don’t need fTemp FUNCTION FILE c2f <- function(c) { return(32) }
  24. First Test - The Simplest Thing... TEST FILE testZeroC <-

    function() { checkEquals(32, c2f(0)) } FUNCTION FILE c2f <- function(c) { return(32) }
  25. Second Test - More than one temp TEST FILE testZeroC

    <- function() { checkEquals(32, c2f(0))} test35C <- function() { checkEquals(95, c2f(35))} Red - 32 ≠ 95 FUNCTION FILE c2f <- function(c) { return(32) }
  26. Second Test - More than one temp TEST FILE testZeroC

    <- function() { checkEquals(32, c2f(0))} test35C <- function() { checkEquals(95, c2f(35))} Green- 32 = 32 & 95 = 95 FUNCTION FILE c2f <- function(c) { return(9/5 * c + 32) }
  27. Second Test - More than one temp TEST FILE testZeroC

    <- function() { checkEquals(32, c2f(0))} test35C <- function() { checkEquals(95, c2f(35))} Refactor? FUNCTION FILE c2f <- function(c) { return(9/5 * c + 32) }
  28. Third Test - Exceptions? TEST FILE testZeroC <- function() {

    checkEquals(32, c2f(0))} test35C <- function() { checkEquals(95, c2f(35))} testStringC <- function() { checkException(c2f('x'))} FUNCTION FILE c2f <- function(c) { return(9/5 * c + 32) }
  29. References RUnit: http://cran.r-project.org/web/packages/RUnit/index.html http://cran.r-project.org/web/packages/RUnit/RUnit.pdf http://cran.r-project.org/web/packages/RUnit/vignettes/RUnit.pdf http://master.bioconductor.org/developers/how-to/unitTesting-guidelines/ Katas with TDD: https://vimeo.com/43734265

    http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata http://www.peterprovost.org/blog/2012/05/02/kata-the-only-way-to-learn-tdd/ History of Unit Testing: http://c2.com/cgi/wiki?TenYearsOfTestDrivenDevelopment http://shebanator.com/2007/08/21/a-brief-history-of-test-frameworks/ http://history.nasa.gov/computers/Ch8-2.html