Let’s TDD a Simple App in PHP

Let’s TDD a Simple App in PHP

Tutorial Details
  • Language: PHP
  • Difficulty: Intermediate
  • Estimated Completion Time: 1-3 Hours
This entry is part 5 of 12 in the Test-Driven PHP Session
« PreviousNext »

In this tutorial, I will present an end-to-end example of a simple application – made strictly with TDD in PHP. I will walk you through each step, one at a time, while explaining the decisions I made in order to get the task done. The example closely follows the rules of TDD: write tests, write code, refactor.


Step 1 - Introduction to TDD & PHPUnit

Test Driven Development (TDD)

TDD is a “test-first” technique to develop and design software. It is almost always used in agile teams, being one of the core tools of agile software development. TDD was first defined and introduced to the professional community by Kent Beck in 2002. Since then, it has become an accepted – and recommended – technique in everyday programming.

TDD has three core rules:

  1. You are not allowed to write any production code, if there is not a failing test to warrant it.
  2. You are not allowed to write more of a unit test than is strictly necessary to make it fail. Not compiling / running is failing.
  3. You are not allowed to write more production code than is strictly necessary to make the failing test pass.

PHPUnit

PHPUnit is the tool that allows PHP programmers to perform unit testing, and practice test-driven development. It is a complete unit testing framework with mocking support. Even though there are a few alternative choices, PHPUnit is the most used and most complete solution for PHP today.

To install PHPUnit, you can either follow along with the previous tutorial in our “TDD in PHP” session, or you can use PEAR, as explained in the official documentation:

  • become root or use sudo
  • make sure you have the latest PEAR: pear upgrade PEAR
  • enable auto discovery: pear config-set auto_discover 1
  • install PHPUnit: pear install pear.phpunit.de/PHPUnit

More information and instructions for installing extra PHPUnit modules can be found in the official documentation.

Some Linux distributions offer phpunit as a precompiled package, though I always recommend an installation, via PEAR, because it ensures that the most recent and up-to-date version is installed and used.

NetBeans & PHPUnit

If you’re a fan of NetBeans, you can configure it to work with PHPUnit by following these steps:

  • Go to NetBeans’ configuration (Tools / Options)
  • Select PHP / Unit Testing
  • Check that the “PHPUnit Script” entry points to a valid PHPUnit executable. If it does not, NetBeans will tell you this, so if you don’t see any red notices on the page, you are good to go. If not, look for the PHPUnit executable on your system and enter its path in the input field. For Linux systems, this path is typically /usr/bin/phpunit.

If you do not use an IDE with unit testing support, you can always run your test directly from the console:

cd /my/applications/test/folder
phpunit

Step 2 - The Problem to Solve

Our team is tasked with the implementation of a “word wrap” feature.

Let’s assume that we are part of a large corporation, which has a sophisticated application to develop and maintain. Our team is tasked with the implementation of a “word wrap” feature. Our clients don’t wish to see horizontal scroll bars, and it’s out job to comply.

In that case, we need to create a class that is capable of formatting an arbitrary bit of text provided as input. The result should be word wrapped at a specified number of characters. The rules of word wrapping should follow the behavior of other every-day applications, like text editors, web page text areas, etc. Our client does not understand all the rules of word wrapping, but they know they want it, and they know it should work in the same way that they’ve experienced in other apps.


Step 3 - Planning

TDD helps you achieve a better design, but it does not eliminate the need for up-front design and thinking.

One of the things that many programmers forget, after they start TDD, is to think and plan beforehand. TDD helps you achieve a better design most of the time, with less code and verified functionality, but it does not eliminate the need for up-front design and human thinking.

Every time you need to solve a problem, you should set aside time to think about it, to imagine a little design – nothing fancy – but enough to get you started. This part of the job also helps you to imagine and guess possible scenarios for the logic of the application.

Let’s think about the basic rules for a word wrap feature. I suppose some un-wrapped text will be given to us. We will know the number of characters per line and we will want it to be wrapped. So, the first thing that comes to my mind is that, if the text has more characters than the number on one line, we should add a new line instead of the last space character that is still on the line.

Okay, that would sum up the behavior of the system, but it is much too complicated for any test. For example, what about when a single word is longer than the number of characters allowed on a line? Hmmm… this looks like an edge case; we can’t replace a space with a new line since we have no spaces on that line. We should force wrap the word, effectively splitting it into two.

These ideas should be clear enough to the point that we can start programming. We’ll need a project and a class. Let’s call it Wrapper.


Step 4 - Starting the Project and Creating the First Test

Let’s create our project. There should be a main folder for source classes, and a Tests/ folder, naturally, for the tests.

The first file we will create is a test within the Tests folder. All our future test will be contained within this folder, so I will not specify it explicitly again in this tutorial. Name the test class something descriptive, but simple. WrapperTest will do for now; our first test looks something like this:

require_once dirname(__FILE__) . '/../Wrapper.php';

class WrapperTest extends PHPUnit_Framework_TestCase {

	function testCanCreateAWrapper() {
		$wrapper = new Wrapper();
	}

}

Remember! We are not allowed to write any production code before a failing test – not even a class declaration! That’s why I wrote the first simple test above, called canCreateAWrapper. Some consider this step useless, but I consider it to be a nice opportunity to think about the class we are going to create. Do we need a class? What should we call it? Should it be static?

When you run the test above, you will receive a Fatal Error message, like the following:

PHP Fatal error:  require_once(): Failed opening required '/path/to/WordWrapPHP/Tests/../Wrapper.php' (include_path='.:/usr/share/php5:/usr/share/php') in /path/to/WordWrapPHP/Tests/WrapperTest.php on line 3

Yikes! We should do something about it. Create an empty Wrapper class in the project’s main folder.

class Wrapper {}

That’s it. If you run the test again, it passes. Congratulations on your first test!


Step 5 - The First Real Test

So we have our project set up and running; now we need to think about our first real test.

What would be the simplest…the dumbest…the most basic test that would make our current production code fail? Well, the first thing that comes to mind is “Give it a short enough word, and expect the result to be unchanged.” This sounds doable; let’s write the test.

require_once dirname(__FILE__) . '/../Wrapper.php';

class WrapperTest extends PHPUnit_Framework_TestCase {

	function testDoesNotWrapAShorterThanMaxCharsWord() {
		$wrapper = new Wrapper();
		assertEquals('word', $wrapper->wrap('word', 5));
	}

}

That looks fairly complicated. What does “MaxChars” in the function name mean? What does 5 in the wrap method refer to?

I think something is not quite right here. Isn’t there a simpler test that we can run? Yes, there certainly is! What if we wrap … nothing – an empty string? That sounds good. Delete the complicated test above, and, instead, add our new, simpler one, shown below:

require_once dirname(__FILE__) . '/../Wrapper.php';

class WrapperTest extends PHPUnit_Framework_TestCase {

	function testItShouldWrapAnEmptyString() {
		$wrapper = new Wrapper();
		$this->assertEquals('', $wrapper->wrap(''));
	}

}

This is much better. The name of the test is easy to understand, we have no magic strings or numbers, and most of all, IT FAILS!

Fatal error: Call to undefined method Wrapper::wrap() in ...

As you can observe, I deleted our very first test. It is useless to explicitly check if an object can be initialized, when other tests also need it. This is normal. With time, you will find that deleting tests is a common thing. Tests, especially unit tests, have to run fast – really fast… and frequently – very frequently. Considering this, eliminating redundancy in tests is important. Imagine that you run thousands of tests every time you save the project. It should take no more than a couple of minutes, maximum, for them to run. So, don’t be terrified to delete a test, if necessary.

Getting back to our production code, let’s make that test pass:

class Wrapper {

	function wrap($text) {
		return;
	}

}

Above, we’ve added absolutely no more code than is necessary to make the test pass.


Step 6 - Pressing On

Now, for the next failing test:

	function testItDoesNotWrapAShortEnoughWord() {
		$wrapper = new Wrapper();
		$this->assertEquals('word', $wrapper->wrap('word', 5));
	}

Failure message:

Failed asserting that null matches expected 'word'.

And the code that makes it pass:

	function wrap($text) {
		return $text;
	}

Wow! That was easy, wasn’t it?

While we are in the green, observe that our test code can begin to rot. We need to refactor a few things. Remember: always refactor when your tests pass; this is the only way that you can be certain that you’ve refactored correctly.

First, let’s remove the duplication of the initialization of the wrapper object. We can do this only once in the setUp() method, and use it for both tests.

class WrapperTest extends PHPUnit_Framework_TestCase {

	private $wrapper;

	function setUp() {
		$this->wrapper = new Wrapper();
	}

	function testItShouldWrapAnEmptyString() {
		$this->assertEquals('', $this->wrapper->wrap(''));
	}

	function testItDoesNotWrapAShortEnoughWord() {
		$this->assertEquals('word', $this->wrapper->wrap('word', 5));
	}

}

The setup method will run before each new test.

Next, there are some ambiguous bits in the second test. What is ‘word’? What is ’5′? Let’s make it clear so that the next programmer who reads these tests doesn’t have to guess.

Never forget that your tests are also the most update-to-date documentation for your code.

Another programmer should be able to read the tests as easily as they would read the documentation.

	function testItDoesNotWrapAShortEnoughWord() {
		$textToBeParsed = 'word';
		$maxLineLength = 5;
		$this->assertEquals($textToBeParsed, $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

Now, read this assertion again. Doesn’t that read better? Of course it does. Don’t be afraid of lengthy variable names for your tests; auto-completion is your friend! It’s better to be as descriptive as possible.

Now, for the next failing test:

	function testItWrapsAWordLongerThanLineLength() {
		$textToBeParsed = 'alongword';
		$maxLineLength = 5;
		$this->assertEquals("along\nword", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

And the code that makes it pass:

	function wrap($text, $lineLength) {
		if (strlen($text) > $lineLength)
			return substr ($text, 0, $lineLength) . "\n" . substr ($text, $lineLength);
		return $text;
	}

That’s the obvious code to make our last test pass. But be careful – it is also the code that makes our first test to not pass!

We have two options to fix this problem:

  • modify the code – make the second parameter optional
  • modify the first test – and make it call the code with a parameter

If you choose the first option, making the parameter optional, that would present a little problem with the current code. An optional parameter is also initialized with a default value. What could such a value be? Zero might sound logical, but it would imply writing code just to treat that special case. Setting a very large number, so that the first if statement would not result in true can be another solution. But, what is that number? Is it 10? Is it 10000? Is it 10000000? We can’t really say.

Considering all these, I will just modify the first test:

	function testItShouldWrapAnEmptyString() {
		$this->assertEquals('', $this->wrapper->wrap('', 0));
	}

Again, all green. We can now move on to the next test. Let’s make sure that, if we have a very long word, it will wrap on several lines.

	function testItWrapsAWordSeveralTimesIfItsTooLong() {
		$textToBeParsed = 'averyverylongword';
		$maxLineLength = 5;
		$this->assertEquals("avery\nveryl\nongwo\nrd", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

This obviously fails, because our actual production code wraps only once.

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 'avery
-veryl
-ongwo
-rd'
+verylongword'

Can you smell the while loop coming? Well, think again. Is a while loop the simplest code that would make the test pass?

According to ‘Transformation Priorities’ (by Robert C. Martin), it is not. Recursion is always simpler than a loop and it is much more testable.

	function wrap($text, $lineLength) {
		if (strlen($text) > $lineLength)
			return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
		return $text;
	}

Can you even spot the change? It was a simple one. All we did was, instead of concatenating with the rest of the string, we concatenate with the return value of calling ourself with the rest of the string. Perfect!


Step 7 - Just Two Words

The next simplest test? What about two words can wrap, when there’s a space at the end of the line.

	function testItWrapsTwoWordsWhenSpaceAtTheEndOfLine() {
		$textToBeParsed = 'word word';
		$maxLineLength = 5;
		$this->assertEquals("word\nword", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

That fits nicely. However, the solution may be getting a bit trickier this time.

At first, you might refer to a str_replace() to get rid of the space and insert a new line. Don’t; that road leads to a dead end.

The second most obvious choice would be an if statement. Something like this:

	function wrap($text, $lineLength) {
		if (strpos($text,' ') == $lineLength)
			return substr ($text, 0, strpos($text, ' ')) . "\n" . $this->wrap(substr($text, strpos($text, ' ') + 1), $lineLength);
		if (strlen($text) > $lineLength)
			return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
		return $text;
	}

However, that enters an endless loop, which will cause the tests to error out.

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted

This time, we need to think! The problem is that our first test has a text with a length of zero. Also, strpos() returns false when it can’t find the string. Comparing false with zero … is? It is true. This is bad for us because the loop will became infinite. The solution? Let’s change the first condition. Instead of searching for a space and comparing its position with the line’s length, let’s instead attempt to directly take the character at the position indicated by the line’s length. We will do a substr() only one character long, starting at just the right spot in the text.

	function wrap($text, $lineLength) {
		if (substr($text, $lineLength - 1, 1) == ' ')
			return substr ($text, 0, strpos($text, ' ')) . "\n" . $this->wrap(substr($text, strpos($text, ' ') + 1), $lineLength);
		if (strlen($text) > $lineLength)
			return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
		return $text;
	}

But, what if the space is not right at the end of line?

	function testItWrapsTwoWordsWhenLineEndIsAfterFirstWord() {
		$textToBeParsed = 'word word';
		$maxLineLength = 7;
		$this->assertEquals("word\nword", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

Hmm… we have to revise our conditions again. I am thinking that we will, after all, need that search for the position of the space character.

	function wrap($text, $lineLength) {
		if (strlen($text) > $lineLength) {
			if (strpos(substr($text, 0, $lineLength), ' ') != 0)
				return substr ($text, 0, strpos($text, ' ')) . "\n" . $this->wrap(substr($text, strpos($text, ' ') + 1), $lineLength);
			return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
		}
		return $text;
	}

Wow! That actually works. We moved the first condition inside the second one so that we avoid the endless loop, and we added the search for space. Still, it looks rather ugly. Nested conditions? Yuck. It’s time for some refactoring.

	function wrap($text, $lineLength) {
		if (strlen($text) <= $lineLength)
			return $text;
		if (strpos(substr($text, 0, $lineLength), ' ') != 0)
			return substr ($text, 0, strpos($text, ' ')) . "\n" . $this->wrap(substr($text, strpos($text, ' ') + 1), $lineLength);
		return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
	}

That’s better better.


Step 8 - What About Multiple Words?

Nothing bad can happen as a result of writing a test.

The next simplest test would be to have three words wrapping on three lines. But that test passes. Should you write a test when you know it will pass? Most of the time, no. But, if you have doubts, or you can imagine obvious changes to the code that would make the new test fail and the others pass, then write it! Nothing bad can happen as a result of writing a test. Also, consider that your tests are your documentation. If your test represents an essential part of your logic, then write it!

Further, the fact the tests we came up with are passing is an indication that we are getting close to a solution. Obviously, when you have a working algorithm, any test that we write will pass.

Now – three words on two lines with the line ending inside the last word; now, that fails.

	function testItWraps3WordsOn2Lines() {
		$textToBeParsed = 'word word word';
		$maxLineLength = 12;
		$this->assertEquals("word word\nword", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

I nearly expected this one to work. When we investigate the error, we get:

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'word word
-word'
+'word
+word word'

Yep. We should wrap at the rightmost space in a line.

	function wrap($text, $lineLength) {
		if (strlen($text) <= $lineLength)
			return $text;
		if (strpos(substr($text, 0, $lineLength), ' ') != 0)
			return substr ($text, 0, strrpos($text, ' ')) . "\n" . $this->wrap(substr($text, strrpos($text, ' ') + 1), $lineLength);
		return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
	}

Simply replace the strpos() with strrpos() inside the second if statement.


Step 9 - Other Failing Tests? Edge Cases?

Things are getting trickier. It’s fairly hard to find a failing test … or any test, for that matter, that was not yet written.

This is an indication that we are quite close to a final solution. But, hey, I just thought of a test that will fail!

	function testItWraps2WordsOn3Lines() {
		$textToBeParsed = 'word word';
		$maxLineLength = 3;
		$this->assertEquals("wor\nd\nwor\nd", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

But, I was wrong. It passes. Hmm… Are we done? Wait! What about this one?

	function testItWraps2WordsAtBoundry() {
		$textToBeParsed = 'word word';
		$maxLineLength = 4;
		$this->assertEquals("word\nword", $this->wrapper->wrap($textToBeParsed, $maxLineLength));
	}

It fails! Excellent. When the line has the same length as the word, we want the second line to not begin with a space.

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 'word
-word'
+ wor
+d'

There are several solutions. We could introduce another if statement to check for the starting space. That would fit in with the rest of the conditionals that we’ve created. But, isn’t there a simpler solution? What if we just trim() the text?

	function wrap($text, $lineLength) {
		$text = trim($text);
		if (strlen($text) <= $lineLength)
			return $text;
		if (strpos(substr($text, 0, $lineLength), ' ') != 0)
			return substr ($text, 0, strrpos($text, ' ')) . "\n" . $this->wrap(substr($text, strrpos($text, ' ') + 1), $lineLength);
		return substr ($text, 0, $lineLength) . "\n" . $this->wrap(substr($text, $lineLength), $lineLength);
	}

There we go.


Step 10 - We Are Done

At this point, I can’t invent any failing test to write. We must be done! We’ve now used TDD to build a simply, but useful, six-line algorithm.

A few words on stopping and “being done.” If you use TDD, you force yourself to think about all sorts of situations. You then write tests for those situations, and, in the process, begin to understand the problem much better. Usually, this process results in an intimate knowledge of the algorithm. If you can’t think of any other failing tests to write, does this mean that your algorithm is perfect? Not necessary, unless there is a predefined set of rules. TDD does not guarantee bug-less code; it merely helps you write better code that can be better understood and modified.

Even better, if you do discover a bug, it’s that much easier to write a test that reproduces the bug. This way, you can ensure that the bug never occurs again – because you’ve tested for it!


Final Notes

You may argue that this process is not technically “TDD.” And you’re right! This example is closer to how many everyday programmers work. If you want a true “TDD as you mean it” example, please leave a comment below, and I’ll plan to write one in the future.

Thanks for reading!

Tags: Testing
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://54grad.de Karsten

    The first example is not a real unit-test and I think, it will produce a “no test found” warning in PHPUnit?
    There is no assert in that function.

    Instead of canCreateAWrapper(), I would start with something like:
    public function testInitModel()
    {
    $wrapper = new Wrapper();
    $this->assertInstanceOf(‘Wrapper’, $wrapper);
    }

    UnitTest functions have to start with the word “test” (e.g. testCanCreateAWrapper) or an Annotation in den docblock @test. Otherwise PHPUnit won’t process these functions.

    Reference:

    The tests are public methods that are named test*.
    Alternatively, you can use the @test annotation in a method’s docblock to mark it as a test method.

    http://www.phpunit.de/manual/3.6/en/writing-tests-for-phpunit.html

    • Patkos Csaba
      Author

      Yes, you are right. That was intended to be called “testItCanCreateAWraper()”. Somehow I’ve lost the “test” on the first 2 tests between code and the tutorial. It’s strange because it was in the code, copy-paste is not always that reliable. ;)

      On the other hand, I would argue that you need “$this->assertInstanceOf(‘Wrapper’, $wrapper)”. The assertion has the presumption that you can actually create a $wrapper object.

      The main reason for the first test is to force one to think about, invent and create the Wrapper() class and not to test that the object we created with new Wrapper() is in fact an instance of Wrapper().

      • Willian

        I don’t think you should test implementation (creating a new object, for example …) … I think you should always test behavior.
        Nice post. Congratulations and thank you!

    • Richie

      Actually a simpler assertion in the above test would be

      $this->assertNotNull($wrapper);

      The assertInstanceOf() method is a more specific test not required here

  • http://inkwell.dotink.org Matthew J. Sahagian

    Let’s not!

    I’m gonna start doing DDD, Documentation Driven Development, where I write my documentation first. Then I write all my tests. Then I write the code. In my mind TDD, while getting you to “think about the code”, fails miserably at getting you to think about the code in a fuller context, namely, because it’s inherently tied to a particular type of testing.

    I like my APIs when I write documentation and/or at least high level conceptual examples of how I want my code to look when using the library or whatever else first. I don’t like my APIs when I’m ad-hoc-ing method names which represent “units.”

    • waterloomatt

      Agreed. I try to do this approach at a module level. I think about how I want the user to interact with it, add the public (static) methods and build the code around that.

      TDD definitely has a place and lands in the middle. Too often I find, developers spend their time getting the algorithms fast and clean, and the end user end up with a horrible mess that they need to “experiment with” to get the results.

      1. Document – high level
      2. Design the public interface for your code and document it
      3. Write tests to make sure the public interface does what it is supposed to
      4. Write the core to pass the tests
      5. Refactor core to make readable and document

    • Patkos Csaba
      Author

      There are two approaches you can take when you do TDD. You can do bottom-up as in this example, when you start by the components and build a system as a puzzle. This is what you don’t like.

      But, you can do top-down development also, start by mocking the interfaces to your API, and work you way down to the last class. Maybe this can work for you.

      Writing _all_ your tests first is also a little bit strange. You may write all the acceptance tests upfront based on your documentation. But how do you write a test for some obscure class 5 levels down in the design you are not even aware of at the beginning? Do you do all your design upfront?

      TDD is not _only_ unit testing.

      I am not trying to convince you, you do as you know best. By the way, how do you keep your documentation in sync with your code? Are you the only programmer on the project? What if there where 10 programmers?

      • http://inkwell.dotink.org Matthew J. Sahagian

        Most documentation I’ve worked on is wiki based, although now I’ve just started to actually include it as part of the project source in markdown format (which my wiki stuff was anyway). So if I change something about a particular piece of code, the docs I need to change are in the same working copy and I can update them so it makes sense, as can anyone else.

        In short, my docs are in sync the same way my code is in sync… git.

      • K

        Patkos, I agree with everything you said before and in your response to what I wrote re BDD.

        My missive (re stepping back towards the original problem that TDD is trying to solve) was addressed (in my head anyway) to Matthew.

        Unfortunatelly there was no button to respond to his comment (It’s just really annoying that you can’t reply to a second level response) – so it looked like I was addressing you… sorry for the confusion.

    • K

      It feels like you are stepping towards one of the original problems TDD was designed to solve.

      You should look at Behaviour Driven Development (or TDD done right as some like to call it).

      The original framework was written in Ruby – and one of the original talks is here:
      http://www.youtube.com/watch?v=XOkHh8zF33o

      There are now new BDD frameworks like Cucumber (again, Ruby) which are quite elegant.
      I believe there’s a PHP BDD framework as well, here: http://behat.org/

      • Patkos Csaba
        Author

        Yes, there is Behat for BDD with PHP.

        My opinion on BDD is that it’s very good on a higher level. It reflects more behavior and it is more business oriented. I see it as a great way to test the API of you application and use it for Acceptance Testing, Behavior Testing and other more high level testing of the system. These test usually exercise a considerable chunk of the whole application and for this reason they are slow.

        TDD is more Unit Testing oriented. You need your tests to run fast, really fast. Anything more than just a few seconds is annoying.
        Also Unit Testing is for developers to verify a small chunk of code, that’s why TDD is so cool. Unit tests are written by developers for developers.

        Behavior / Acceptence tests are written by QA (usually) for Managers.

        Of course, this does not stop you or any developer to use a BDD framework for Unit Testing or a unit test framework for Acceptance Testing.

  • samir

    thank you
    make us something like this in video
    bye

  • http://creativeloop.co.zw Maston Mbewe

    Please lets have this tutorial in Video.

    Thanks

    • Patkos Csaba
      Author

      I am already in talks with the Nettuts editor about this subject. Probably first I will do a simpler example as a test video and if all goes well you will see several videos about TDD in the future.

      • l

        I hold you to your promise.

  • http://www.elimcmakin.com Eli McMakin

    The thing I noticed is that the author assumes the reader knows how to run a test. How do we create this test in NetBeans?

    • Patkos Csaba
      Author

      Yes I presumed. Sorry for that, some things are obvious for me and I omit the fact that they may be unknown for others.

      Here is the quick way:
      - under the menu item “Run” there is a “Test Project” entry. Click it.
      - NetBeans will ask for a “Test Folder”
      - point it to the folder called “Tests” in our example.

      Here are all the things you can do at Step 4. above to configure testing from project properties in NetBeans:
      - right click on you project on the left
      - select “Properties”
      - point the “Test Folder” input to the folder called “Tests” in the project
      - click OK
      - under the menu item “Run” there is a “Test Project” entry.

      • http://www.elimcmakin.com Eli McMakin

        OK, so I got it working. But there are other things presumed. For example, what is this function “assertEquals”? That seems like it might be an important thing to go over. This is not something in the SPL, as far as I can tell.

  • James

    You missed out refactoring. This is an integral part of TDD. If you just stop when all your tests pass, you are going to have test that works, but isn’t maintainable. You need to refactor your working solution into a more maintainable, and robust solution. Something I have noticed PHP programmers don’t tend to do.

    • Patkos Csaba
      Author

      Yes, you are right. I left out refactoring intentionally. The same way I intentionally left out explanation about what “assertEquals” means.

      I targeted this tutorial for peoples who can find out or know what “assertEquals” means but at the same rime they are at level where refactoring is not very common for them.

      I proposed tutorials on these themes before writing this one, but it was preferred to start with this and continue towards the ideas and problems rose by you here.

  • http://www.2world1life.com Bogdan Robert Adrian

    Hello. I need help. I have an idea and i want to put into practice. My country is 10 years behind the others in Europe. And I Want to change, the path to knowledge to Evolve faster. I have an idea, I know it’s a small step, but will have a good effect on them. I create design a site for general knowledge but I stuck to the script php. I want help with the following script: The user can answer general knowledge questions and gain points. All to be saved in a database. Admin to be able to add questions.
    Sorry for my English . Contact my please !

  • @Bogdan Robert Adrian

    Bogdan Robert Adrian: Przeciez są gotowe systemy w php,sciagasz, klikasz w konfiguracje i gotowe.

    Pierwsze znalezione w google: http://www.vivalogo.com/vl-resources/quora-stackoverflow-clone-scripts.htm