[Sciviews-commits] r87 - in pkg/svUnit: . inst inst/doc

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Sun Jan 11 16:44:21 CET 2009


Author: phgrosjean
Date: 2009-01-11 16:44:21 +0100 (Sun, 11 Jan 2009)
New Revision: 87

Added:
   pkg/svUnit/inst/doc/
   pkg/svUnit/inst/doc/svUnit.Rnw
   pkg/svUnit/inst/doc/svUnit.lyx
   pkg/svUnit/inst/doc/svUnit.pdf
   pkg/svUnit/inst/doc/svUnit_KomodoRUnit.png
   pkg/svUnit/inst/doc/svUnit_wikiReport.png
Modified:
   pkg/svUnit/DESCRIPTION
   pkg/svUnit/NEWS
Log:
Vignette added to svUnit package

Modified: pkg/svUnit/DESCRIPTION
===================================================================
--- pkg/svUnit/DESCRIPTION	2009-01-02 15:00:40 UTC (rev 86)
+++ pkg/svUnit/DESCRIPTION	2009-01-11 15:44:21 UTC (rev 87)
@@ -4,8 +4,8 @@
 Suggests: svGUI, datasets, utils
 Description: A complete unit test system and functions to implement its GUI part
 Version: 0.6-2
-Date: 2008-10-23
+Date: 2009-01-12
 Author: Philippe Grosjean
 Maintainer: Philippe Grosjean <phgrosjean at sciviews.org>
-License: GPL 2 or above
+License: GPL (>= 2)
 URL: http://www.sciviews.org/SciViews-R

Modified: pkg/svUnit/NEWS
===================================================================
--- pkg/svUnit/NEWS	2009-01-02 15:00:40 UTC (rev 86)
+++ pkg/svUnit/NEWS	2009-01-11 15:44:21 UTC (rev 87)
@@ -1,5 +1,9 @@
 = svUnit News
 
+== svUnit 0.6-2
+* There is now a vignette for the svUnit package.
+
+
 == svUnit 0.6-1
 * Correction of a bug in Windows relative to path separators \\ versus /,
 especially usinf tempdir().

Added: pkg/svUnit/inst/doc/svUnit.Rnw
===================================================================
--- pkg/svUnit/inst/doc/svUnit.Rnw	                        (rev 0)
+++ pkg/svUnit/inst/doc/svUnit.Rnw	2009-01-11 15:44:21 UTC (rev 87)
@@ -0,0 +1,1163 @@
+%% LyX 1.5.6 created this file.  For more info, see http://www.lyx.org/.
+%% Do not edit unless you really know what you are doing.
+\documentclass[a4paper,english]{article}
+\usepackage{lmodern}
+\renewcommand{\sfdefault}{lmss}
+\renewcommand{\ttdefault}{lmtt}
+\usepackage[T1]{fontenc}
+\usepackage[latin9]{inputenc}
+\usepackage{framed}
+\usepackage{url}
+\usepackage{graphicx}
+
+\makeatletter
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
+\newenvironment{lyxSchunk}{}{}
+\newenvironment{lyxlist}[1]
+{\begin{list}{}
+{\settowidth{\labelwidth}{#1}
+ \setlength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}[1]{##1\hfil}}}
+{\end{list}}
+\newenvironment{lyxcode}
+{\begin{list}{}{
+\setlength{\rightmargin}{\leftmargin}
+\setlength{\listparindent}{0pt}% needed for AMS classes
+\raggedright
+\setlength{\itemsep}{0pt}
+\setlength{\parsep}{0pt}
+\normalfont\ttfamily}%
+ \item[]}
+{\end{list}}
+\providecommand{\proglang}[1]{\textsf{#1}}
+\providecommand{\code}[1]{\texttt{#1}}
+\providecommand{\command}[1]{\texttt{#1}}
+\providecommand{\pkg}[1]{{\fontseries{b}\selectfont #1}}
+\providecommand{\var}[1]{{\normalfont\textsl{#1}}}
+\providecommand{\kbd}[1]{\textsf{\textsc{#1}}}
+\providecommand{\file}[1]{`\textsf{#1}'}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
+% \VignetteIndexEntry{svUnit - A framework for unit testing in R}
+
+\makeatother
+
+\usepackage{babel}
+
+\begin{document}
+
+\title{svUnit - A framework for unit testing in R }
+
+
+\date{Version 0.6-2, 2009-01-12}
+
+
+\author{Philippe Grosjean (phgrosjean at sciviews.org)}
+
+\maketitle
+
+\section{Introduction}
+
+Unit testing (see \url{http://en.wikipedia.org/wiki/Unit_test}) is
+an approach successfully used to develop software, and to ease code
+refactoring for keeping bugs to the minimum. It is also the insurance
+that the software is doing the right calculation (quality insurance).
+Basically, a test just checks if the code is running and is producing
+the correct answer/behavior in a given situation. As such, unit tests
+are build in \proglang{R} package production because all examples
+in documentation files, and perhaps, test code in \file{/tests} subdirectory
+are run during the checking of a package (\command{R CMD check <Pkg>}).
+However, the \proglang{R} approach lacks a certain number of features
+to allow optimal use of unit tests as in extreme programming (test
+first \textendash{} code second): 
+
+\begin{itemize}
+\item Tests are related to package compilation and cannot easily be run
+independently (for instance, for functions developed separately).
+\item Once a test fails, the checking process is interrupted. Thus one has
+to correct the bug and launch package checking again... and perhaps
+get caught by the next bug. It is a long and painful process.
+\item There is no way to select one or several tests: all are run or not
+(depending on command line options) during package testing.
+\item It is impossible to program in \proglang{R} in a test driven development
+(\url{http://en.wikipedia.org/wiki/Test- driven_development}, write
+tests first). 
+\item Consequently, the {}`test-code-simplify' cycle is not accessible
+yet to \proglang{R} programmer, because of the lack of an interactive
+and flexible testing mechanism providing immediate, or quasi immediate
+feedback about changes made.
+\item We would like also to emphasize that test suites are not only useful
+to check code, they can also be used to check data, or the pertinence
+of analyses.
+\end{itemize}
+
+\subsection{Unit testing in R without svUnit}
+
+Besides the \char`\"{}regular\char`\"{} testing mechanism of \proglang{R}
+packages, one can find the \pkg{RUnit} package on CRAN (\url{http://cran.r-project.org}).
+Another package used to provide test unit, \pkg{butler}, but it is
+not maintained any more and seems to have given up in favor of \pkg{RUnit}.
+\pkg{RUnit} implements the following features: 
+
+\begin{itemize}
+\item \textbf{Assertions} for tests (\code{checkEquals()}, \code{checkEqualsNumeric()},
+\code{checkIdentical()} and \code{checkTrue()}) and negative tests
+(tests that check error conditions, \code{checkException()}).
+\item Assertions are grouped into \proglang{R} functions to form one \textbf{test
+function}. It is easy to temporarily inactivate one or more tests
+by commenting lines in the function. To avoid forgetting tests that
+are commented out later on, there is special function, named \code{DEACTIVATED()},
+that tags the test with a reminder for your deactivated items (i.e.,
+this is written in the test protocol).
+\item A series of test functions (whose name typically start with \code{test}...)
+are collected together in a sourceable \proglang{R} code file (name
+starting with \code{runit}...) on disk. This file is called a \textbf{test
+unit}.
+\item A \textbf{test suite} (object \var{RUnitTestSuite}) is a special
+object defining a battery of tests to run. It points to one or several
+directories containing test units. A test suite is defined by \code{defineTestSuite()}.
+\item One or more test suites can be run by calling \code{runTestSuite()}.
+There is a convenient shortcut to define and run a test suite constituted
+by only one test unit by using the function \code{runTestFile()}.
+Once the test is run, a \var{RUnitTestData} object is created that
+contains all the information collected from the various tests run.
+\item One can print a synthetic report (how many test units, test functions,
+number of errors, fails and deactivated item), or get a more extensive
+\code{summary()} of the test with indication about the tests that
+failed. The function \code{printTextProtocol()} does the same, while
+\code{printHTMLProtocol()} produces a report in HTML format.
+\item \pkg{RUnit} contains also functions to determine which code is run
+in the original function when tested, in order to detect the parts
+of the code not covered by the test suite (code coverage functions
+\code{inspect()} and \code{tracker()}).
+\end{itemize}
+As complete and nice as \pkg{RUnit} is, there is no tools to integrate
+the test suite in a given development environment (IDE) or graphical
+user interface (GUI). In particular, there is no real-time reporting
+mechanism used to easy the test-code-simplify cycle. The way tests
+are implemented and run is left to the user, but the implementation
+suggests that the authors of \pkg{RUnit} mainly target batch execution
+of the tests (for instance, nightly check of code in a server), rather
+that real-time interaction with the tests.
+
+There is also no integration with the \char`\"{}regular\char`\"{}
+\command{R CMD check} mechanism of \proglang{R} in \pkg{RUnit}.
+There is an embryo of organization of these tests units to make them
+compatible with the \command{R CMD check} mechanism of \proglang{R}
+on the R Wiki (\url{http://wiki.r-project.org/rwiki/doku.php?id=developers:runit}).
+This approach works well only on Linux/Unix systems, but needs to
+be adapted for Windows. 
+
+
+\subsection{Unit testing framework for R with svUnit}
+
+Our initial goal, in the context of the EU project UNCOVER, was to
+implement a GUI layer on top of \pkg{RUnit}, and to integrate test
+units as smoothly as possible in a code editor, as well as, making
+tests easily accessible and fully compatible with \command{R CMD check}
+on all platforms supported by \proglang{R}. Ultimately, the test
+suite should be easy to create, to use interactively, and should be
+able to test functions in a complex set of \proglang{R} packages.
+
+However, we encountered several difficulties while trying to enhance
+\pkg{RUnit} mechanism. When we started to work on this project, \pkg{RUnit}
+(version 0.4-17) did not allow to subclass its objects. Moreover,
+its \var{RUnitTestData} object is optimized for quick testing, but
+not at all for easy reviewing of its content: it is a list of lists
+of lists,... requiring embedded for loop and \code{lapply()}/\code{sapply()}
+procedures to extract its content. Finally, the concept of test units
+as sourceable files on disk is a nice idea, but it is too rigid for
+quick writing of test cases for objects not associated (yet) with
+\proglang{R} packages.
+
+We did a first implementation of the \pkg{RUnit} GUI based on these
+objects, before realizing that it is really not designed for such
+an use. So, we decide to write a completely different unit testing
+framework in \proglang{R}: \pkg{svUnit}, but we make it test code
+compatible with \pkg{RUnit} (i.e., the engine and objects used are
+totally different, but the test code run in \pkg{RUnit} or \pkg{svUnit}
+can be interchangeable).
+
+Finally, \pkg{svUnit} is also designed to be integrated in the SciViews
+R GUI (\url{http://www.sciviews.org/SciViews-K}), on top of Komodo
+Edit (\url{http://www.activestate.com/komodo_edit}), and to approach
+extreme programming practices with automatic code testing while you
+write it. A rather simple interface is provided to link and pilot
+\pkg{svUnit} from any GUI/IDE, and the Komodo Edit implementation
+could be use as example to program similar integration panels for
+other R GUIs. \pkg{svUnit} also formats its report with creole wiki
+syntax. It is directly readable, but it can also be displayed in a
+much nicer way using any wiki engine compatible with the creole wiki
+language. It is thus rather easy to write test reports in wiki servers,
+possibly through nightly automatic process for your code, if you like.
+
+This vignette is a guided tour of svUnit, showing you its features
+and the various ways you can use it to test your \proglang{R} code.
+
+
+\section{Installation}
+
+The \pkg{svUnit} package is not available on CRAN (\url{http://cran.r-project.org})
+yet, but it is available from R-Forge (\url{http://r-forge.r-project.org}).
+You can download it from \proglang{R} by:
+
+\begin{lyxSchunk}
+<<eval = FALSE>>=
+
+install.packages("svUnit",repos="http://R-Forge.R-project.org")
+
+@
+\end{lyxSchunk}
+This package has no dependence other than R $\geq$ 1.9.0. However,
+if you would like to use its interactive mode in a GUI editor, you
+must also install Komodo Edit and SciViews. The procedure is explaned
+at \url{http://www.sciviews.org/SciViews-K}.
+
+Once the svUnit package is installed, you can check it is working
+well with the following example code:
+
+\begin{lyxSchunk}
+<<>>=
+
+library(svUnit)
+
+Square <- function(x) return(x^2)
+
+test(Square) <- function() {
+
+    checkEquals(9, Square(3))
+
+    checkEquals(10, Square(3))  # This intentionally fails
+
+    checkEquals(9, SSSquare(3)) # This intentionally raises error
+
+    checkEquals(c(1, 4, 9), Square(1:3))
+
+    checkException(Square("xx"))
+
+}
+
+clearLog()
+
+(runTest(Square))
+
+@
+\end{lyxSchunk}
+%
+\begin{framed}
+Although test unit code is compatible with both \pkg{svUnit} and
+\pkg{RUnit}, do not load both packages in \proglang{R} memory at
+the same time, or you will badly mix incompatible code between them!
+\end{framed}
+
+
+
+\section{Overview of svUnit}
+
+You ensure that code you write in \proglang{R} functions does work
+as you want by defining a battery of tests that will compare the output
+of your code with reference values. In \pkg{svUnit}, the simplest
+way to define such a battery of tests is by attaching it to functions
+loaded in \proglang{R} memory%
+\footnote{In fact, you can attach \pkg{svUnit} tests to any kind of \proglang{R}
+object, not only function. This could be useful to test S3/S4 objects,
+or even, datasets. %
+}. Of course, you can also define batteries of tests that are independent
+of any \proglang{R} object, or that check several of them together
+(integration tests). Here is a couple of examples:
+
+\begin{lyxSchunk}
+<<>>=
+
+library(svUnit)
+
+# Create two R functions that include their test own cases
+
+Square <- function(x) return(x^2)
+
+test(Square) <- function() {
+
+    checkEquals(9, Square(3))
+
+    checkEquals(c(1, 4, 9), Square(1:3))
+
+    checkException(Square("xx"))
+
+}
+
+
+
+Cube <- function(x) return(x^3)
+
+test(Cube) <- function() {
+
+    checkEquals(27, Cube(3))
+
+    checkEquals(c(1, 8, 28), Cube(1:3))
+
+    checkException(Cube("xx"))
+
+}
+
+
+
+# Add a separate test case object (not attached to a particular object)
+
+test_Integrate <- svTest(function() {
+
+    checkTrue(1 < 2, "check1")
+
+    v <- 1:3  # The reference
+
+    w <- 1:3  # The value to compare to the reference
+
+    checkEquals(v, w)
+
+})  
+
+@
+\end{lyxSchunk}
+When you run a test in \pkg{svUnit}, it logs its results in a centralized
+logger. The idea is to get a central repository for tests that you
+can manipulate as you like (print, summarize, convert, search, display
+in a GUI, etc.). If you want to start new tests, you should first
+clean this logger by \code{cleanLog()}. At any time, the logger is
+accessible by \code{Log()}, and a summary of its containt is displayed
+using \code{summary(Log())}. So, to run test for your \code{Square()}
+function as well as your \code{test\_Integrate} integration test,
+you simply do the following:
+
+\begin{lyxSchunk}
+<<>>=
+
+clearLog()
+
+runTest(Square)
+
+runTest(test_Integrate)
+
+Log()
+
+@
+\end{lyxSchunk}
+From this report, you see that all your tests succeed. Note that \pkg{svUnit}
+is making the difference between a test that \textbf{fails} (the code
+is run correctly, but the result is different that what was expected)
+and code that raises \textbf{error} (it was not possible to run the
+test because its code is incorrect, or for some other reasons). Note
+also that the function \code{checkException()} is designed to explicitly
+test code that should stop() in R. So, if that test does not raises
+an exception, it is considered to have failed. This is useful to check
+that your functions correctly trap wrong arguments, for instance,
+like in \code{checkException(Square(\char`\"{}xx\char`\"{}))} here
+above (a character string is provided where a numerical value is expected).
+
+Now, let's look what happen if we test the \code{Cube()} function
+without clearing the logger:
+
+\begin{lyxSchunk}
+<<>>=
+
+runTest(Cube)
+
+Log()
+
+@
+\end{lyxSchunk}
+We note two things:
+
+\begin{enumerate}
+\item The results of the tests on \code{Cube()} are added to the previous
+report. So, it is possible to build rather easily reports that summarize
+tests on one or several (possibly) complex codes, by adding tests
+results in the logger. \pkg{svUnit} does this naturally and transparently.
+Starting a new report is equally simple: just use \code{clearLog()}.
+\item This time, we have one test that fails. We expected \code{c(1, 8, 28)}
+from \code{Cube(1:3)} in \code{checkEquals(c(1, 8, 28), Cube(1:3))},
+however, we got \code{c(1, 8, 29)}. Of course, in this case, the
+\code{Cube()} function is correct and it is our test that is misspecified,
+but that was done purposedly to show how \pkg{svUnit} outputs test
+failures.
+\end{enumerate}
+
+\subsection{Assertions in svUnit}
+
+The most basic item is an \textbf{assertion} represented by a \code{checkxxx()}
+function in \pkg{svUnit}/\pkg{RUnit}. Five such functions are currently
+defined:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\code{checkEquals(current, target)}}] determines if data in \var{target}
+is the same as data in \var{current}.
+\item [{\code{checkEqualsNumeric(current, target)}}] does the same but
+allows for a better comparison for numbers (variation allowed within
+a tolerance window).
+\item [{\code{checkIdentical(current, target)}}] checks whether two \proglang{R}
+objects are strictly identical.
+\item [{\code{checkTrue(expr)}}] only succeed if \var{expr} is \code{TRUE}.
+Note a difference in \pkg{svUnit} and \pkg{RUnit} (at least, in
+version 0.4-17). The \pkg{RUnit} function is not vectorized and \var{expr}
+must return a single atomic logical value. The corresponding \pkg{svUnit}
+function also accepts a vector of logical values. In this case, all
+elements must be \code{TRUE} for the test to succeed. When you make
+sure that \var{expr} always returns a single logical value (for instance
+by using \code{all(expr)}), both functions should be compatible between
+the two packages.
+\item [{\code{checkException(expr)}}] verifies that a given code raises
+an exception (in \proglang{R}, it means that a line of code with
+\code{stop()} is executed).
+\item [{\code{DEACTIVATED()}}] both makes sure that all tests following
+this instruction (in a test function, see next paragraph) are deactivated,
+and inserts a notification in the logger that these tests are deactivated
+as a reminder.
+\end{lyxlist}
+For all these functions, you have an additional optional argument
+\var{msg} indicating a short message to print in front of each text
+in the report. These functions return invisibly: \code{TRUE} if the
+test succeeds, \code{FALSE} if it fails (code is executed correctly,
+but does not pass the test), and \code{NA} if there was an error
+(the \proglang{R} code was not executed correctly). Moreover, these
+functions record the results, the context of the test and the timing
+in a logger (object \var{svSuiteData}inheriting from \var{environment})
+called \code{.Log} and located in the user\textquoteright{}s workspace.
+So, executing a series of assertions and getting a report is simply
+done as (in its simplest form, you can use the checkxxx() functions
+directly at the command line):
+
+\begin{lyxSchunk}
+<<>>=
+
+clearLog()
+
+checkTrue(1 == log(exp(1)))
+
+checkException(log("a"))
+
+checkTrue(1 == 2)
+
+Log()
+
+@
+\end{lyxSchunk}
+As you can see, the \code{checkxxx()} functions work hand in hand
+with the test logger. Indeed, the \code{checkxxx()} functions return
+the result of the test invisibly, which is discarded in the present
+case (assign the result to a variable and print it, for instance to
+see this result). Indeed, they are better used for their side-effect
+of adding an entry to the svUnit logger.
+
+The last command \code{Log()} prints its content. You see how a report
+is printed, with a first part being a short summary by categories
+(assertions run at the command line are placed in the \var{eval}
+category, because there is no better context known for them. Usually,
+those assertions should be placed in test functions, or in test units,
+as we will see later in this manual). A detailed report on the tests
+that failed or raised an error is printed at the end of the report. 
+
+Of course, the same report is much easier to manipulate from within
+the graphical tree in the Komodo\textquoteright{}s \kbd{R Unit} tab,
+but this text report from \proglang{R} has the advantage of being
+independent from any GUI, and from Komodo. It can also be generated
+in batch mode. Last, but not least, it uses a general Wiki formatting
+called creole wiki (\url{http://www.wikicreole.org/wiki/Creole1.0}).
+Figure \ref{fig:wikireport} illustrates the way the same report looks
+like in DokuWiki with the creole plugin (\url{http://www.wikicreole.org/wiki/DokuWiki})
+installed. Note the convenient table of content that lists here a
+clickable and quick summary of all tests run. From this point, it
+is relatively easy to define nightly cron task jobs on a server to
+run a script that executes these tests and update a wiki page (look
+at your particular wiki engine documentation to determine how you
+can access wiki pages on the command line).
+
+%
+\begin{figure}
+
+\centering{}
+
+\includegraphics{svUnit_wikiReport}
+
+\caption{\label{fig:wikireport}a svUnit test report as it appears when inserted
+in a wiki page (DokuWiki engine with the \code{creole} plugin installed).
+Note the summary of results at the top lef of the page, and the clickable
+table of contents with exhaustive and detailed entries to easily navigate
+to the test results you want to consult). Timing of the test is also
+clearly indicated, since it is a complementary but important information
+(if a test succeeds, but calculation is way too long, it is good to
+know it)!}
+
+\end{figure}
+
+
+
+
+
+\subsection{Manipulating the logger data}
+
+\pkg{svUnit} provides a series of methods and tools to manipulate
+the log file from the command line, in particular, \code{stats()},
+\code{summary()}, \code{metadata()}, \code{ls()}:
+
+\begin{lyxSchunk}
+<<>>=
+
+# Clear test exclusion list (to run test suites defined in svUnit)
+
+options(svUnit.excludeList = NULL)
+
+# Clear the log
+
+clearLog()
+
+# Run all currently defined tests, including test suites in svUnit
+
+runTest(svSuiteList(), name = "AllTests")
+
+# Get some statistics
+
+stats(Log())[, 1:3]
+
+# A slightly different presentation than print
+
+summary(Log())
+
+# Metadata collected on the machine where tests are run
+
+metadata(Log())
+
+# List content of the log
+
+ls(Log())
+
+@
+\end{lyxSchunk}
+As you can see, \code{ls()} lists all components recorded in the
+test suite. Each component is a \var{svTestData} object inheriting
+from \var{data.frame}, and it can be easily accessed through the
+\code{\$} operator. There are, of course similar methods defined
+for those \var{svTestData} objects, like \code{print()}, \code{summary()},
+and \code{stats()}:
+
+\begin{lyxSchunk}
+<<>>=
+
+myTest <- Log()$testCube
+
+class(myTest)
+
+myTest
+
+summary(myTest)
+
+stats(myTest)
+
+@
+\end{lyxSchunk}
+As the logger inherits from \var{environment}, you can manage individual
+test data the same way as objects in any other environment. For instance,
+if you want to delete a particular test data without touching to the
+rest, you can use:
+
+\begin{lyxSchunk}
+<<>>=
+
+ls(Log())
+
+rm(test_R, envir = Log())
+
+ls(Log())
+
+@
+\end{lyxSchunk}
+As we will see in the following section, \pkg{svUnit} proposes several
+means to organize individual assertions in various modules within
+a logical organization: \textbf{test functions}, \textbf{test units}
+and \textbf{test suites}. This organization is inspired from \pkg{RUnit},
+but with additional ways of using tests in interactive sessions (for
+instance, the ability to attach a test to the objects to be tested).
+
+
+\subsection{Test function}
+
+The first organization level for grouping assertions together is the
+\textbf{test function}. A test function is a function without arguments
+whose name must start with \var{test}. It typically contains a series
+of assertions applied to one object, method, or function to be checked
+(this is not obligatory, assertions are not restricted to one object,
+but good practices strongly suggest it). Here is an example:
+
+\begin{lyxSchunk}
+<<>>=
+
+test_function <- function() {
+
+    checkTrue(1 < 2, "check1")
+
+    v <- 1:3  # The reference
+
+    w <- 1:3  # The value to compare to the reference
+
+    checkEquals(v, w)
+
+}
+
+test_function <- as.svTest(test_function)
+
+is.svTest(test_function)
+
+@
+\end{lyxSchunk}
+A test function should be made a special object called \var{svTest},
+so that \pkg{svUnit} can recognize it. This \var{svTest} object,
+is allowed to live alone (for instance, loaded in \code{.GlobalEnv},
+defined in a \proglang{R} script, etc... in \pkg{svUnit}, while
+in \pkg{RUnit}, it must be located in a unit test). In \pkg{svUnit}
+(not \pkg{RUnit}), you run this test simply by using \code{runTest()},
+which returns the results invisibly (and you are supposed to access
+results from the logger):
+
+\begin{lyxSchunk}
+<<>>=
+
+clearLog()
+
+runTest(test_function)
+
+Log()
+
+@
+\end{lyxSchunk}
+Now, a test function is most likely designed to test an \proglang{R}
+object. \pkg{svUnit} also provides facilities to attach the test
+function to the object to be tested. Hence, the test cases and the
+tested object conveniently form a single entity that one can manipulate,
+copy, save, reload, etc. with all the usual tools in \proglang{R}.
+This association is simply made using \code{test(myobj) <-}:
+
+\begin{lyxSchunk}
+<<>>=
+
+# A very simple function
+
+Square <- function(x) return(x^2)
+
+
+
+# A test case to associate with the Square() function
+
+test(Square) <- function() {
+
+    checkEquals(9, Square(3))
+
+    checkEquals(c(1, 4, 9), Square(1:3))
+
+    checkException(Square("xx"))
+
+}
+
+is.test(Square) # Does this object contain tests?
+
+@
+\end{lyxSchunk}
+One can retrieve the test associated with the object by using:
+
+\begin{lyxSchunk}
+<<>>=
+
+test(Square)
+
+@
+\end{lyxSchunk}
+And of course, running the test associated with an object is as easy
+as:
+
+\begin{lyxSchunk}
+<<>>=
+
+runTest(Square)
+
+Log()  # Remember we didn't clear log, so we add to the report here
+
+@
+\end{lyxSchunk}
+Now that you master test functions, you should discover how you can
+group them in logical \textbf{units}, and associate them to \proglang{R}
+packages.
+
+
+\subsection{Test units}
+
+A \textbf{unit} is a coherent piece of software that can be tested
+separately from the rest. Typically, a \proglang{R} package is a
+structured way to compile and distribute such code units in \proglang{R}.
+Hence, we need a mean to organize tests related to this unit conveniently.
+
+Since a package can contain several functions, data frames, or other
+objects, our unit should collect together individual test functions
+related to each of these objects composing our package. Also, the
+test unit should accommodate the well-define organization of a package,
+and should integrate also in the already existing testing features
+of \proglang{R}, in particular with \command{R CMD check}. In both
+\pkg{RUnit}, and \pkg{svUnit}, one can define such test units, and
+they are made code compatible between them (at least for version 0.4-17
+of \pkg{RUnit}).
+
+A test unit a a sourceable text file that contains one or more test
+functions, plus possibly \code{.setUp()} and \code{.tearDown()}
+functions (see the online help for further information on these special
+functions). In \pkg{RUnit}, you must write such test unit files from
+scratch. With \pkg{svUnit}, you can \char`\"{}promote\char`\"{} one
+or several test functions (associated to other objects, or \char`\"{}living\char`\"{}
+alone as separate \var{svTest} objects) by using the \code{makeUnit()}
+method. Here is how you promote the test associated with our \code{Square()}
+function to a simple unit test containing only one function:
+
+\begin{lyxSchunk}
+<<>>=
+
+# Create a test unit on disk and view it
+
+unit <- makeUnit(Square)
+
+file.show(unit, delete.file = TRUE)
+
+@
+\end{lyxSchunk}
+You got the following file whose name must start with \var{runit},
+with an \var{.R} extension (\file{runit{*}.R}), and located by default
+in the temporary environment. Specify another directory with the \code{dir =}
+argument of \code{makeUnit()} for a more permanent record of this
+test unit file. Note also that \code{.setUp()} and \code{.tearDown()}
+functions are constructed automatically for you. They specify the
+context of these tests. This context is used by the GUI in Komodo
+to locate the test function and the code being tested.
+
+\begin{lyxcode}
+\#\#~Test~unit~'Square'
+
+
+
+.setUp~<-
+
+function~()~\{
+
+~~~~\#\#~Specific~actions~for~svUnit:~prepare~context
+
+~~~~if~(\char`\"{}package:svUnit\char`\"{}~\%in\%~search())~\{
+
+~~~~~~~~.Log~<-~Log()~\#\#~Make~sure~.Log~is~created
+
+~~~~~~~~.Log\$..Unit~<-~\char`\"{}/tmp/RtmpBoZnId/runitSquare.R\char`\"{}
+
+~~~~~~~~.Log\$..File~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Obj~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Tag~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Msg~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~rm(..Test,~envir~=~.Log)
+
+~~~~\}
+
+\}
+
+
+
+.tearDown~<-
+
+function~()~\{
+
+~~~~\#\#~Specific~actions~for~svUnit:~clean~up~context
+
+~~~~if~(\char`\"{}package:svUnit\char`\"{}~\%in\%~search())~\{
+
+~~~~~~~~.Log\$..Unit~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..File~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Obj~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Tag~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~.Log\$..Msg~<-~\char`\"{}\char`\"{}
+
+~~~~~~~~rm(..Test,~envir~=~.Log)
+
+~~~~\}
+
+\}
+
+
+
+\char`\"{}testSquare\char`\"{}~<-
+
+function()~\{
+
+~~~~checkEquals(9,~Square(3))
+
+~~~~checkEquals(c(1,~4,~9),~Square(1:3))
+
+~~~~checkException(Square(\char`\"{}xx\char`\"{}))
+
+\}
+\end{lyxcode}
+Compatibility of these test unit files between \pkg{RUnit} and \pkg{svUnit}
+was a major concern in the design of \pkg{svUnit}. Consequently,
+code specific to \pkg{svUnit} (for managing the context of the test)
+is embedded in a \code{if (\char`\"{}package:svUnit\char`\"{} \%in\% search())}.
+That way, if \pkg{svUnit} is not loaded in memory, this code is not
+executed. \emph{Note that you should avoid loading in memory both
+\pkg{svUnit} and \pkg{RUnit} at the same time. If you do so, you
+will most likely crash your tests.}
+
+You will see further that it is possible to write much more complex
+test units with the same \code{makeUnit()} method, starting from
+test suites. But for the moment, let\textquoteright{}s discuss a little
+bit how such test units should be organized in \proglang{R} package.
+
+If you intend to associate test units to \proglang{R} package, you
+should respect the following conventions:
+
+\begin{itemize}
+\item Name your test units \file{runit{*}.R}.
+\item Place them in the \file{/inst/unitTests} subdirectory of the package
+sources, or in one of its subdirectories. If you place them in a subdirectory
+of \file{/inst/unitTests}, then you define secondary unit tests,
+for instance, for more optional detailed testing of the package. Always
+keep in mind that all \file{runit{*}.R} files in a directory will
+be run one after the other. So, if you want to make subgroups you
+would like to dissociate, define subdirectories.
+\item When the package will be compiled, all these test units will be located
+in \file{/unitTests}.
+\end{itemize}
+If you respect these conventions, \pkg{svUnit} knows where package
+unit tests are located and will be able to find and run them quite
+easily. See, for instance, the examples in the \pkg{svUnit} package.
+
+So, with test units associated to packages, you have a very convenient
+way to run these tests, including from the Komodo \kbd{R Unit} tab
+panel. With just a little bit more coding you can also include these
+test units in the \command{R CMD check} process of your packages.
+You do that by means of examples (we prefer to use \textbf{examples},
+instead of \file{/tests} in the \command{R CMD check} process, because
+examples offer a more flexible way to run tests and you can also run
+them in interactive sessions through the \code{example()} function,
+which is not the case for \file{/tests}). Here is what you do to
+associate some or all of your unit tests to \command{R CMD check}
+(illustrated with the \pkg{svUnit} example):
+
+\begin{itemize}
+\item Define a \file{.Rd} file in \file{/man} called \file{unitTests.Rd}
+(or whatever name you prefer).
+\item Fill the \file{.Rd} file, making sure that you define an alias as
+\var{unitTests.myPKG}. Also place a little bit of information telling
+how users can run your test in an interactive session.
+\item The important part of this file is, of course, the \code{\textbackslash{}examples\{\}}
+section. You must first clear the log, then run each test, and then,
+call the \code{errorLog()} function. That function looks if one or
+more tests failed or raised an error. In this case, it stops execution
+of the example and causes a dump in \command{R CMD check} with a
+report on the tests that failed. That way, providing that you have
+the \pkg{svUnit} package installed in the machine where you run \command{R CMD check},
+your test units will be included nicely in the checking process of
+your packages, that is, they will run silently each time you test
+your package if no error occurs, but will produce a detailed report
+in case of problems.
+\item Here is how your \file{.Rd} file should looks like:
+\end{itemize}
+\begin{lyxcode}
+\textbackslash{}name\{unitTests\}
+
+\textbackslash{}alias\{unitTests.svUnit\}
+
+
+
+\textbackslash{}title\{~Unit~tests~for~the~package~svUnit~\}
+
+
+
+\textbackslash{}description\{~Performs~unit~tests~defined~in~this
+
+~~package~by~running~\textbackslash{}code\{example(unitTests.svUnit)\}.
+
+~~Tests~are~in~\textbackslash{}code\{runit{*}.R\}~files~Located~in~the
+
+~~'/unitTests'~subdirectory~or~one~of~its
+
[TRUNCATED]

To get the complete diff run:
    svnlook diff /svnroot/sciviews -r 87


More information about the Sciviews-commits mailing list