Having never written any automated tests, how should I start behaviour-driven development? [closed]
Asked Answered
S

2

6

I've been programming for years in plenty of languages and like to think I'm generally pretty good at it. However, I haven't ever written any automated testing: no unit tests, no TDD, no BDD, nothing.

I've tried to start writing proper test suites for my projects. I can see the theoretical value of being able to test all the code in a project automatically after making any changes. I can see how test frameworks like RSpec and Mocha should make setting up and running said tests reasonably easy, and I like the DSLs they provide for writing tests.

But I have never managed to write an actual unit test for any part of my code. Stuff I write never seems to be very testable in a way that's actually useful.

  • Functions don't seem to be very callable outside the context in which they're used. Many functions I write make HTTP-request calls, or database queries, or some other non-easily-testable call.
  • Some functions return strings of HTML. I can compare the HTML string against a hardcoded version of the same string, but that only seems to limit my ability to change that part of the code. Plus having loads of HTML in my test code is a mess.
  • I can pass mock/spy objects into a method, and make sure they get certain method calls, but as far as I can tell that's only testing implementation details of the method I'm "testing".

How would I go about getting started with proper BDD testing? (I'd preferably like to do this using Mocha and Node.js, but general advice on BDD is fine too.)

Socle answered 14/2, 2013 at 9:9 Comment(0)
B
3

It looks like the main question you're asking is, "how do I write testable code"?

Being a fan of object oriented programming I know I'm biased, but in my experience it's far easier to test code that is written in an OO style. The reason for this is that unit tests are meant to test small, isolated components of a system, and well designed object oriented code (mostly) provides this.

I agree that functions are often linked to the context that they're in, making them difficult to test. I don't have a lot of experience with functional programming, but I know that context is often passed around in some sort of variable, making it difficult to separate concerns of functions.

With OO programming, I have successfully tested objects that wrap around HTTP requests, database queries, etc, by mocking the object that does the actual network request to return a known set of data. You then test that your wrapper object handles that data in the right way. You can also test for failures and unexpected data. Another way of doing this is by setting up a local server that you use instead of the normal endpoint, but this gives your test suite an external dependency, which should be avoided when possible.

When testing HTML, many people don't do this at all, due to the highly changeable nature of the view layer. However, there are some things that are really worth testing, but never the full string of HTML - as you've discovered, just a tiny change would mean that the whole test breaks. What are you really testing in that case, that two strings in separate parts of your code base are the same?

The best thing to do is the load the HTML string from your function/object into an HTML parser library, and you can normally use Xpath or CSS selectors to check for tags with particular classes, IDs or other attributes, and check the number of elements that match certain requirements. Rspec has this built in (the have_tag() method), as do many testing libraries.

Something else you might like to look at is integration testing (e.g. Capybara, Selenium). This will load your web app with a JavaScript engine, so you can check for HTML elements and also JavaScript events.

On the whole mocking/stubbing thing, you generally only want to do this with objects that are dependencies of the object you're testing. Otherwise you can pretty much manipulate anything to assert as true!

As for resources on testing, I'd recommend looking at test driven development books even if you don't plan to practice TDD. The main reason is that they throw you head first into testing. here are a few:

  1. Kent Beck's book Test Driven Development: By Example
  2. Free ebook on TDD with PHP, Practical PHP Testing
  3. This website, Art of Unit Testing
  4. Slideshare - just search for unit testing or BDD and read as many as possible!
  5. David Chelimsky et. al.: The RSpec Book
Billiot answered 14/2, 2013 at 9:9 Comment(2)
+1 for the TDDBE book mention. i'd just like to add that it's not OOP that enables testing, it's resolute separation of concerns, and substitutable dependencies.Electrograph
@justsomebody I agree, that's why I gave the caveat of me being biased :)Declass
E
0

TDD or BDD, doesn't matter, they're isomorphic as evidenced by Mocha, where they're just pluggable frontends.

I can pass mock/spy objects into a method, and make sure they get certain method calls, but as far as I can tell that's only testing implementation details of the method I'm "testing".

That's precisely the point of unit testing: you're testing just the code you're testing, not more. The problem with testing more is that a failing test usually does not flag the problem precisely. The promise of unit testing is that you can largely avoid debugging. Too coarse tests nullify that. Of course, a balance is required, anybody with any UT experience will tell you that it's easy to fall into the i've-stubbed-the-thing-i-wanted-to-test-and-didn't-notice trap, your tests could be all green and the actual code not working.

Electrograph answered 14/2, 2013 at 10:16 Comment(3)
"TDD or BDD, doesn't matter, they're isomorphic as evidenced by Mocha, where they're just pluggable frontends." Heh, good to know. I couldn't really figure out how they differed but liked BDD's describe/it style better, hence my asking about it. :PSocle
"That's precisely the point of unit testing: you're testing just the code you're testing, not more." I've tended to find a test like "does this method call someotherobject.methodfoo" rather superfluous, because of course it calls that method, since that's what it's coded to do. I'm beginning to realise that's completely missing the point.Socle
btw, i'm not saying that you should test everything. unit testing is not excused from the law of diminishing returns. as i wrote, testability is mostly a function of separation of concerns and substitutable dependencies. in OOP, this means avoiding "god" objects, singletons, hardcoded collaborator objects. if your Customer objects need access to the database, they should accept an instance of DbConnection into the constructor. DbConnection should be an interface, not a concrete class. etc.Electrograph

© 2022 - 2024 — McMap. All rights reserved.