Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
565,63 KB
Nội dung
178
Chapter 6 Unit Testing
“;
$this->numSentences = 2;
$this->numWords = 16;
$this->numSyllables = 24;
$this->object = new Text_Statistics($this->sample);
}
function _ _construct($name) {
parent::_ _construct($name);
}
}
Sure enough, the bug is there. Mr. matches as the end of a sentence.You can try to
avoid this problem by removing the periods from common abbreviations.To do this, you
need to add a list of common abbreviations and expansions that strip the abbreviations of
their punctuation.You make this a static attribute of Text_Statistics and then sub-
stitute on that list during analyze_line. Here’s the code for this:
class Text_Statistics {
//
static $abbreviations = array(‘/Mr\./’ =>’Mr’,
‘/Mrs\./i’ =>’Mrs’,
‘/etc\./i’ =>’etc’,
‘/Dr\./i’ =>’Dr’,
);
//
protected function analyze_line($line) {
// replace our known abbreviations
$line = preg_replace(array_keys(self::$abbreviations),
array_values(self::$abbreviations),
$line);
preg_match_all(“/\b(\w[\w’-]*)\b/”, $line, $words);
foreach($words[1] as $word) {
$word = strtolower($word);
$w_obj = new Text_Word($word);
$this->numSyllables += $w_obj->numSyllables();
$this->numWords++;
if(!isset($this->_uniques[$word])) {
$this->_uniques[$word] = 1;
}
else {
$this->uniqWords++;
}
}
preg_match_all(“/[.!?]/”, $line, $matches);
$this->numSentences += count($matches[0]);
}
}
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
179
Unit Testing in a Web Environment
The sentence count is correct now, but now the syllable count is off. It seems that Mr.
counts as only one syllable (because it has no vowels).To handle this, you can expand the
abbreviation expansion list to not only eliminate punctuation but also to expand the
abbreviations for the purposes of counting syllables. Here’s the code that does this:
class Text_Statistics {
//
static $abbreviations = array(‘/Mr\./’ =>’Mister’,
‘/Mrs\./i’ =>’Misses’, //Phonetic
‘/etc\./i’ =>’etcetera’,
‘/Dr\./i’ =>’Doctor’,
);
//
}
There are still many improvements you can make to the Text_Statistics routine.
The
$silentSyllable and $additionalSyllable arrays for tracking exceptional
cases are a good start, but there is still much work to do. Similarly, the abbreviations list is
pretty limited at this point and could easily be expanded as well.Adding multilingual
support by extending the classes is an option, as is expanding the statistics to include
other readability indexes (for example, the Gunning FOG index, the SMOG index, the
Flesch-Kincaid grade estimation, the Powers-Sumner-Kearl formula, and the FORCAST
Formula). All these changes are easy, and with the regression tests in place, it is easy to
verify that modifications to any one of them does not affect current behavior.
Unit Testing in a Web Environment
When I speak with developers about unit testing in PHP in the past, they often said
“PHP is a Web-centric language, and it’s really hard to unit test Web pages.” This is not
really true, however.
With just a reasonable separation of presentation logic from business logic, the vast
majority of application code can be unit tested and certified completely independently
of the Web.The small portion of code that cannot be tested independently of the Web
can be validated through the
curl extension.
About curl
curl is a client library that supports file transfer over an incredibly wide variety of Internet protocols (for
example, FTP, HTTP, HTTPS, LDAP). The best part about curl is that it provides highly granular access to the
requests and responses, making it easy to emulate a client browser. To enable curl, you must either con-
figure PHP by using with-curl if you are building it from source code, or you must ensure that your
binary build has curl enabled.
We will talk about user authentication in much greater depth in Chapter 13,“User
Authentication and Session Security” but for now let’s evaluate a simple example.You
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
180
Chapter 6 Unit Testing
can write a simple inline authentication system that attempts to validate a user based on
his or her user cookie. If the cookie is found, this HTML comment is added to the
page:
<! crafted for NAME !>
First, you need to create a unit test.You can use curl to send a user=george cookie
to the authentication page and then try to match the comment that should be set for
that user. For completeness, you can also test to make sure that if you do not pass a
cookie, you do not get authenticated. Here’s how you do all this:
<?php
require_once “PHPUnit/Framework/TestCase.php”;
// WebAuthCase is an abstract class which just sets up the
// url for testing but runs no actual tests.
class WebAuthTestCase extends PHPUnit_Framework_TestCase{
public $curl_handle;
public $url;
function _ _construct($name) {
parent::_ _construct($name);
}
function setUp() {
// initialize curl
$this->curl_handle = curl_init();
// set curl to return the response back to us after curl_exec
curl_setopt($this->curl_handle, CURLOPT_RETURNTRANSFER, 1);
// set the url
$this->url =
“http://devel.omniti.com/auth.php”;
curl_setopt($this->curl_handle, CURLOPT_URL, $this->url);
}
function tearDown() {
// close our curl session when we
’re finished
curl_close($this->curl_handle);
}
}
// WebGoodAuthTestCase implements a test of successful authentication
class WebGoodAuthTestCase extends WebAuthTestCase {
function _ _construct($name) {
parent::_ _construct($name) ;
}
function testGoodAuth() {
$user = ‘george’;
// Consturct a user=NAME cookie
$cookie = “user=$user;”;
// Set the cookie to be sent
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
181
Unit Testing in a Web Environment
curl_setopt($this->curl_handle, CURLOPT_COOKIE, $cookie);
// execute our query
$ret = curl_exec($this->curl_handle);
$this->assertRegExp(“/<! crafted for $user >/”, $ret);
}
}
// WebBadAuthTestCase implements a test of unsuccessful authentication
class WebBadAuthTestCase extends WebAuthTestCase {
function _ _construct($name) {
parent::_ _construct($name);
}
function testBadAuth() {
// Don’t pass a cookie
curl_setopt($this->curl_handle, CURLOPT_COOKIE, $cookie);
// execute our query
$ret = curl_exec($this->curl_handle);
if(preg_match(“/<! crafted for /”, $ret)) {
$this->fail();
}
else {
$this->pass();
}
}
}
if(realpath($_SERVER[‘PHP_SELF’]) == _ _FILE_ _) {
require_once “PHPUnit/Framework/TestSuite.php”;
require_once “PHPUnit/TextUI/TestRunner.php”;
$suite = new PHPUnit_Framework_TestSuite(‘WebGoodAuthTestCase’);
$suite->addTestSuite(“WebBadAuthTestCase”);
PHPUnit_TextUI_TestRunner::run($suite);
}
?>
In contrast with the unit test, the test page is very simple—just a simple block that adds
a header when a successful cookie is matched:
<HTML>
<BODY>
<?php
if($_COOKIE[user]) {
echo “<! crafted for $_COOKIE[user] >”;
}
?>
<?php print_r($_COOKIE) ?>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
182
Chapter 6 Unit Testing
Hello World.
</BODY>
</HTML>
This test is extremely rudimentary, but it illustrates how you can use curl and simple
pattern matching to easily simulate Web traffic. In Chapter 13, “User Authentication and
Session Security,” which discusses session management and authentication in greater
detail, you use this WebAuthTestCase infrastructure to test some real authentication
libraries.
Further Reading
An excellent source for information on unit testing is Test Driven Development By
Example by Kent Beck (Addison-Wesley).The book uses Java and Python examples, but
its approach is relatively language agnostic. Another excellent resource is the JUnit
homepage, at www.junit.org.
If you are interested in learning more about the Extreme Programming methodology,
see Testing Extreme Programming, by Lisa Crispin and Tip House (Addison-Wesley), and
Extreme Programming Explained: Embrace Change, by Kent Beck (Addison-Wesley), which
are both great books.
Refactoring: Improving the Design of Existing Code, by Martin Fowler (Addison-Wesley),
is an excellent text that discusses patterns in code refactoring.The examples in the book
focus on Java, but the patterns are very general.
There are a huge number of books on qualitative analysis of readability, but if you are
primarily interested in learning about the actual formulas used, you can do a Google
search on readability score to turn up a number of high-quality results.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
7
Managing the Development
Environment
FOR MANY PROGRAMMERS, MANAGING A LARGE SOFTWARE project is one of the least
exciting parts of the job. For one thing, very little of a programming job involves writing
code. Unlike the normally agile Web development model, where advances are made rap-
idly, project management is often about putting a throttle on development efforts to
ensure quality control. Nevertheless, I find the challenges to be a natural extension of my
work as a programmer. At the end of the day, my job is to make sure that my clients’
Web presence is always functioning as it should be. I need to not only ensure that code
is written to meet their needs but also to guarantee that it works properly and that no
other services have become broken.
Enterprise is a much-bandied buzzword that is used to describe software. In the
strictest definition, enterprise software is any business-critical piece of software. Enterprise is
a synonym for business, so by definition, any business software is enterprise software.
In the software industry (and particularly the Web industry), enterprise is often used to
connote some additional properties:
n
Robust
n
Well tested
n
Secure
n
Scalable
n
Manageable
n
Adaptable
n
Professional
It’s almost impossible to quantify any of those qualities, but they sure sound like some-
thing that any business owner would want. In fact, a business owner would have to
be crazy not to want enterprise software! The problem is that like many buzzwords,
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
184
Chapter 7 Managing the Development Environment
enterprise is a moniker that allows people to brand their software as being the ideal solu-
tion for any problem, without making any real statement as to why it is better than its
competitors. Of course, buzzwords are often rooted in technical concerns before they
become co-opted by marketers.The vague qualities listed previously are extremely
important if you are building a business around software.
In this book you have already learned how to write well-tested software (Chapter 6,
“Unit Testing”). In Chapters 13, “User Authentication and Session Security,” and 14,
“Session Handling,” you will learn about securing software (both from and for your
users). Much of this book is dedicated to writing scalable and robust software in a pro-
fessional manner.This chapter covers making PHP applications manageable.
There are two key aspects to manageability:
n
Change control—Managing any site—large or small—without a well-established
change control system is like walking a tightrope without a safety net.
n
Managing packaging— A close relative of change control, managing packaging
ensures that you can easily move site versions forward and backward, and in a dis-
tributed environment, it allows you to easily bring up a new node with exactly the
contents it should have.This applies not only to PHP code but to system compo-
nents as well.
Change Control
Change control software is a tool that allows you to track individual changes to project files
and create versions of a project that are associated with specific versions of files.This
ability is immensely helpful in the software development process because it allows you to
easily track and revert individual changes.You do not need to remember why you made
a specific change or what the code looked like before you made a change. By examining
the differences between file versions or consulting the commit logs, you can see when a
change was made, exactly what the differences were, and (assuming that you enforce a
policy of verbose log messages) why the change was made.
In addition, a good change control system allows multiple developers to safely work
on copies of the same files simultaneously and supports automatic safe merging of their
changes. A common problem when more than one person is accessing a file is having
one person’s changes accidentally overwritten by another’s. Change control software aims
to eliminate that risk.
The current open source standard for change control systems is Concurrent
Versioning System (CVS). CVS grew as an expansion of the capabilities of Revision
Control System (RCS). RCS was written by Walter Tichy of Purdue University in 1985,
itself an improvement on Source Code Control System (SCSS), authored at ATT Labs in
1975. RCS was written to allow multiple people to work on a single set of files via a
complex locking system. CVS is built on top of RCS and allows for multi-ownership of
files, automatic merging of contents, branching of source trees, and the ability for more
than one user to have a writable copy of the source code at a single time.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
185
Change Control
Alternative to CVS
CVS is not the only versioning system out there. There are numerous replacements to CVS, notably BitKeeper
and Subversion. Both of these solutions were designed to address common frustrations with CVS, but
despite their advanced feature sets, I have chosen to focus on CVS because it is the most widely deployed
open-source change control system and thus the one you are most likely to encounter.
Using CVS Everywhere
It never ceases to amaze me that some people develop software without change control. To me, change
control is a fundamental aspect of programming. Even when I write projects entirely on my own, I always
use CVS to manage the files. CVS allows me to make rapid changes to my projects without needing to keep
a slew of backup copies around. I know that with good discipline, there is almost nothing I can do to my
project that will break it in a permanent fashion. In a team environment, CVS is even more essential. In daily
work, I have a team of five developers actively accessing the same set of files. CVS allows them to work
effectively with very little coordination and, more importantly, allows everyone to understand the form and
logic of one another’s changes without requiring them to track the changes manually.
In fact, I find CVS so useful that I don’t use it only for programming tasks. I keep all my system configura-
tion files in CVS as well.
CVS Basics
The first step in managing files with CVS is to import a project into a CVS repository.
To create a local repository, you first make a directory where all the repository files will
stay.You can call this path
/var/cvs, although any path can do. Because this is a perma-
nent repository for your project data, you should put the repository someplace that gets
backed up on a regular schedule. First, you create the base directory, and then you use
cvs init to create the base repository, like this:
> mkdir /var/cvs
> cvs -d /var/cvs init
This creates the base administrative files needed by CVS in that directory.
CVS on Non-UNIX Systems
The CVS instructions here all apply to Unix-like operating systems (for example, Linux, BSD, OS X). CVS also
runs on Windows, but the syntax differences are not covered here. See http://www.cvshome.org
and http://www.cvsnt.org for details.
To import all the examples for this book, you then use import from the top-level direc-
tory that contains your files:
> cd Advanced_PHP
> cvs -d /var/cvs import Advanced_PHP advanced_php start
cvs import: Importing /var/cvs/books/Advanced_PHP/examples
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
186
Chapter 7 Managing the Development Environment
N books/Advanced_PHP/examples/chapter-10/1.php
N books/Advanced_PHP/examples/chapter-10/10.php
N books/Advanced_PHP/examples/chapter-10/11.php
N books/Advanced_PHP/examples/chapter-10/12.php
N books/Advanced_PHP/examples/chapter-10/13.php
N books/Advanced_PHP/examples/chapter-10/14.php
N books/Advanced_PHP/examples/chapter-10/15.php
N books/Advanced_PHP/examples/chapter-10/2.php
No conflicts created by this import
This indicates that all the files are new imports (not files that were previously in the
repository at that location) and that no problems were encountered.
-d /var/cvs specifies the repository location you want to use.You can alternatively
set the environment variable CVSROOT, but I like to be explicit about which repository I
am using because different projects go into different repositories. Specifying the reposito-
ry name on the command line helps me make sure I am using the right one.
import is the command you are giving to CVS.The three items that follow
(Advanced_PHP advanced_php start) are the location, the vendor tag, and the release
tag. Setting the location to Advanced_PHP tells CVS that you want the files for this proj-
ect stored under
/var/cvs/Advanced_PHP.This name does not need to be the same as
the current directory that your project was located in, but it should be both the name by
which CVS will know the project and the base location where the files are located
when you retrieve them from CVS.
When you submit that command, your default editor will be launched, and you will
be prompted to enter a message.Whenever you use CVS to modify the master reposito-
ry, you will be prompted to enter a log message to explain your actions. Enforcing a pol-
icy of good, informative log messages is an easy way to ensure a permanent paper trail
on why changes were made in a project.You can avoid having to enter the message
interactively by adding
-m “message” to your CVS lines. If you set up strict standards
for messages, your commit messages can be used to automatically construct a change log
or other project documentation.
The vendor tag (
advanced_php) and the release tag (start) specify special branches
that your files will be tagged with. Branches allow for a project to have multiple lines of
development.When files in one branch are modified, the effects are not propagated into
the other branches.
The vendor branch exists because you might be importing sources from a third party.
When you initially import the project, the files are tagged into a vendor branch.You can
always go back to this branch to find the original, unmodified code. Further, because it is
a branch, you can actually commit changes to it, although this is seldom necessary in my
experience. CVS requires a vendor tag and a release tag to be specified on import, so
you need to specify them here. In most cases, you will never need to touch them again.
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
187
Change Control
Another branch that all projects have is HEAD. HEAD is always the main branch of
development for a project. For now, all the examples will be working in the HEAD branch
of the project. If a branch is not explicitly specified, HEAD is the branch in which all
work takes place.
The act of importing files does not actually check them out; you need to check out
the files so that you are working on the CVS-managed copies. Because there is always a
chance that an unexpected error occurred during import, I advise that you always move
away from your current directory, check out the imported sources from CVS, and visual-
ly inspect to make sure you imported everything before removing your original reposi-
tory. Here is the command sequence to check out the freshly imported project files:
> mv Advanced_PHP Advanced_PHP.old
> cvs -d /var/cvs checkout Advanced_PHP
cvs checkout: Updating Advanced_PHP
cvs checkout: Updating Advanced_PHP/examples
U Advanced_PHP/examples/chapter-10/1.php
U Advanced_PHP/examples/chapter-10/10.php
U Advanced_PHP/examples/chapter-10/11.php
U Advanced_PHP/examples/chapter-10/12.php
U Advanced_PHP/examples/chapter-10/13.php
U Advanced_PHP/examples/chapter-10/14.php
U Advanced_PHP/examples/chapter-10/15.php
# manually inspect your new Advanced_PHP
> rm -rf Advanced_PHP.old
Your new Advanced_PHP directory should look exactly like the old one, except that
every directory will have a new CVS subdirectory.This subdirectory holds administrative
files used by CVS, and the best plan is to simply ignore their presence.
Binary Files in CVS
CVS by default treats all imported files as text. This means that if you check in a binary file—for example, an
image—to CVS and then check it out, you will get a rather useless text version of the file. To correctly han-
dle binary file types, you need to tell CVS which files have binary data. After you have checked in your files
(either via import or commit), you can then execute cvs admin -kab <filename> to instruct
CVS to treat the file as binary. For example, to correctly add advanced_php.jpg to your repository, you
would execute the following:
> cvs add advanced_php.jpg
> cvs commit -m ‘this books cover art’ advanced_php.jpg
> cvs admin -kab advanced_php.jpg
Subsequent checkouts of advanced_php.jpg will then behave normally.
Alternatively, you can force CVS to treat files automatically based on their names. You do this by editing the
file CVSROOT/cvswrappers. CVS administrative files are maintained in CVS itself, so you first need to
do this:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
[...]... examples/chapter-7/1 .php: < ?php echo “Hello $_GET[‘name’]”; ?> You have changed it to take name from any request variable: < ?php echo “Hello $_REQUEST[‘name’]”; ?> To commit this change to CVS, you run the following: > cvs commit -m “use any method, not just GET” examples/chapter-7/1 .php Checking in examples/chapter-7/1 .php; /var/cvs /Advanced_ PHP/ examples/chapter-7/1 .php, v < 1 .php new revision: 1.2;... version by using this: > cvs diff -u3 examples/chapter-7/1 .php Index: examples/chapter-7/1 .php =================================================================== RCS file: /var/cvs/books /Advanced_ PHP/ examples/chapter-7/1 .php, v retrieving revision 1.2 diff -u -3 -r1.2 1 .php - 1 .php 2003/08/26 15:40:47 1.2 +++ 1 .php 2003/08/26 16:21:22 @@ -1,3 +1,4 @@ < ?php echo “Hello $_REQUEST[‘name’]”; +echo “\nHow are... following syntax: > cvs diff -u3 -r 1.1 examples/chapter-7/1 .php Index: examples/chapter-7/1 .php =================================================================== RCS file: /var/cvs/books /Advanced_ PHP/ examples/chapter-7/1 .php, v retrieving revision 1.1 diff -u -3 -r1.1 1 .php - 1 .php 2003/08/26 15:37:42 1.1 +++ 1 .php 2003/08/26 16:21:22 @@ -1,3 +1,4 @@ < ?php -echo “Hello $_GET[‘name’]”; +echo “Hello $_REQUEST[‘name’]”;... > cvs diff -u3 -D ‘20 minutes ago’ examples/chapter-7/1 .php Index: examples/chapter-7/1 .php =================================================================== RCS file: /var/cvs /Advanced_ PHP/ examples/chapter-7/1 .php, v retrieving revision 1.2 diff -u -3 -r1.2 1 .php - 1 .php 2003/08/26 15:40:47 1.2 +++ 1 .php 2003/08/26 16:21:22 @@ -1,3 +1,4 @@ < ?php echo “Hello $_REQUEST[‘name’]”; +echo “\nHow are you?”;... -u3 -r 1.1 -r 1.2 examples/chapter-7/1 .php Index: examples/chapter-7/1 .php =================================================================== RCS file: /var/cvs/books /Advanced_ PHP/ examples/chapter-7/1 .php, v retrieving revision 1.1 retrieving revision 1.2 diff -u -3 -r1.1 -r1.2 - 1 .php 2003/08/26 15:37:42 1.1 +++ 1 .php 2003/08/26 15:40:47 1.2 @@ -1,3 +1,3 @@ < ?php -echo “Hello $_GET[‘name’]”; +echo... working directory to hold it.To check out the PROD branch of the Advanced_ PHP repository, you use the following command: > cvs checkout -r PROD Advanced_ PHP To signify that this is a specific branch of the project, it is often common to rename the top-level directory to reflect the branch name, as follows: > mv Advanced_ PHP Advanced_ PHP- PROD Alternatively, if you already have a checked-out copy of... them, as in this example: > cvs update examples/chapter-7/1 .php RCS file: /var/cvs /Advanced_ PHP/ examples/chapter-7/1 .php, v retrieving revision 1.2 retrieving revision 1.3 Merging differences between 1.2 and 1.3 into 1 .php rcsmerge: warning: conflicts during merge cvs update: conflicts found in examples/chapter-7/1 .php C examples/chapter-7/1 .php You need to carefully look at the output of any CVS command... # hello.inc < ?php function hello() { print “Hello World\n”; } ?> and then you change both of these files as follows: # index .php < ?php require_once “hello.inc”; hello(“George”); ?> # hello.inc 201 202 Chapter 7 Managing the Development Environment < ?php function hello($name) { print “Hello $name\n”; } ?> if someone is requesting index .php just as the content push ensues, so that index .php is parsed... PHP files PHP parses every file it needs to execute on every request.This has a number of deleterious effects on performance (which you will learn more about in Chapter 9, “External Performance Tunings”) and also makes it rather unsafe to change files in a running PHP instance.The problem is simple: If you have a file index .php that includes a library, such as the following: # index .php < ?php require_once... use cvs log on the file in question This command shows all the commits for that file, with dates and commit log messages: > cvs log examples/chapter-7/1 .php RCS file: /var/cvs /Advanced_ PHP/ examples/chapter-7/1 .php, v Working file: examples/chapter-7/1 .php head: 1.2 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 2; selected revisions: 2 description: . Advanced_ PHP/ examples
U Advanced_ PHP/ examples/chapter-10/1 .php
U Advanced_ PHP/ examples/chapter-10/10 .php
U Advanced_ PHP/ examples/chapter-10/11 .php
U Advanced_ PHP/ examples/chapter-10/12 .php
U. files:
> mv Advanced_ PHP Advanced_ PHP. old
> cvs -d /var/cvs checkout Advanced_ PHP
cvs checkout: Updating Advanced_ PHP
cvs checkout: Updating Advanced_ PHP/ examples
U