The short question: How do you follow Test-Driven Development on a project that spans multiple languages?

Specifically, I'm writing a web application that uses JavaScript and PHP, and I want to follow the TDD principles, but I'm not sure how to integrate them. Do I run separate test suites for the JS and PHP sections, and use mocks in the JS suite to emulate server responses? Is there a technique for unit testing both components in one run?

This is my first experience using Test-Driven Development, so any advice you can share on how to make it less daunting would be great. The reason I chose it is that as soon as I finished a prototype, the requirements changed, forcing me to change my design. I figured if I'm starting over, I'd like to write more extensible code with built-in regression testing from the start.

I'm writing my PHP tests in SimpleTest and my JavaScript tests in JsTestDriver. I'm used to object-oriented paradigms, so I've got a few classes in PHP, and I'm doing the something similar in JavaScript using prototypal inheritance. I've also started reading this book about TDD in Python and this one about TDD in JavaScript, but from all that I've seen, these don't describe testing an application in full (outside of using something like Selenium or another web driver to perform front-end acceptance testing. Is TDD just not cut out for full-stack developers?

  • 1
    Unless you are being forced to use SimpleTest, I'd recommend switching to PHPUnit. SimpleTest does not seem to be very active and lags behind a bit when it comes to mocking and code coverage. – Cerad Aug 20 '15 at 16:21
up vote 9 down vote accepted

Is there a technique for unit testing both components in one run?

That would actually be the opposite of unit testing - unit testing, especially in TDD style, means to test your components in isolation. Thus the answer is yes, "run separate test suites for the JS and PHP sections", otherwise it is not unit testing and not TDD.

Of course, automated integration tests can test "both components in one run", and you can utilize exactly the tools you already mentioned (like Selenium). But those are typically more complex tests, developed outside the TDD cycles.

The important thing is to distinguish between TDD and ATDD. The AT there stands for "acceptance tests", and this refers to development where you first start with an acceptance test, which is likely to test the entire stack. This is also sometimes called "outside-in test driven development". When people talk about TDD, the "T" there probably refers specifically to unit tests.

A key part of unit tests is isolating the unit under test from its dependencies. This gives you two very important benefits:

  • You can make your tests extremely fast, so you can run them very often as part of a very short feedback cycle. Rather than running them, say, every hour, you should be able to run all your unit tests after every little change, so you instantly get their feedback on whether that change broke anything.

  • Your tests can be targeted at once very specific behavior. If one of them fails, you should be able to determine almost instantly the exact nature of the bug that the test is indicating.

Because of the importance of this isolation, you will automatically want tests to be restricted to much smaller units than your language boundaries force you into. Though it's not a hard and fast rule, you would often expect a single class to be a testable unit. So in terms of TDD, the language issue is more or less irrelevant.

If, on the other hand, you want to do ATDD, make sure you look into resources for this specifically (it's often done as part of BDD, so look at tools targeted at that, too). Here's where something like Selenium would naturally fit in. Usually, when doing ATDD you still write unit tests, and in fact in order to pass each acceptance test you might test drive its implementation with unit tests too. So even if you do want to do ATDD, understanding how to write unit tests is still important.

  • Cool, I've never looked at ATDD but I'm familiar with Behat and Behave for BDD. I appreciate the feedback. Still trying to wrap my head around the micro-test mentality of TDD :) – Chris Olsen Aug 20 '15 at 15:43
  • @ChrisOlsen There's two aspects to get your head around: unit tests (vs. integration or acceptance) and when you write them (before the production code rather than after). If they both seem like strange mentalities, you could try getting to grips with the first before the second. A lot of people don't practice TDD but still insist on very thorough unit test coverage. – Ben Aaronson Aug 20 '15 at 15:46

Also you don't have to use TDD for the full stack of the application. TDD as a development methodology is more suited for the logic parts of an application. The parts that require a more experimental touch, like said the design of the frontend, certains interactions, or the database domain are better done in a traditional style as you don't know yet if that is going to be the final version.

But the logic of the application should not change in the future, and if it does you're facing the much dreaded requirement creep. That makes it perfect for a suite of regression tests to give confidence whenever making a change to the code, which is the ultimate goal of TDD.

Uncle Bob explains this way better than me. https://blog.8thlight.com/uncle-bob/2014/04/30/When-tdd-does-not-work.html

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.