3

I'm working on a boilerplate component that's supposed to generate a working sort-of "getting started" project structure. Think of it, in a web app context, of a component that will generate a Controller, a View and the routes configuration so that once you call it, you can immediately go to the browser, type the route and you'll get the output.

In this boilerplate, one of my class has the responsibility of providing the content of every file it generates (the content of the controller file, the content of the routes file, the content of the views fie, etc).

With such a responsibility, wheareas one method should return the content foo, the test should verify that, in fact, 'foo' is returned.

It ends up looking like this:

// SUT
function getFooFileContent() {
    return 'foo';
}

// test
function testGetFooFileContent() {
    string expected = 'foo';

    string result = sut.getFooFileContent();

    assertEquals(expected, result);
}

The people I'm explaining the reason for this test are new to unit testing, so it's normal for them not to grasp at first sight why is this OK, and have doubts whether this test actually has a value to begin with.

Since I'm in charge of introducing unit testing to my team, I want to explain this backed up by resources that explain this "type" of test in particular, so that I have a solid argument rather than asking them to just accept that kind of test.

PS: For the record, when I started with unit testing (self taught, no mentor sadly), I had the same doubt. In my case I just accepted the test and got to understand its value over the years, when I could finally see the fact that tests are supposed to bind functionality to specifications. It's hard to see that when you're starting.

Update 1:

Due to the fact of my question marked as duplicated for the common should I test a getter/setter? I say:

This is not really a getter and the question is not about whether is worth it or not. The class specification is to provide a template text for a boilerplate. The implementation could very well evolve into having it read the text from a non *.php file, and, in that case, a test would have value as it would bind the specification to the functionality.

The question is how to explain the fact that, even with the current implementation (the one that is just returning a string block) the test itself is correct. I'm specifically asking for resources that backup that argument.

  • 1
    You said it yourself: demonstrate that the test binds functionality to a specification. – Robert Harvey Oct 25 '16 at 23:39
  • 5
    I suspect you'll need a slightly more complex example to convince your peers. Something where a seemingly innocuous change would "inadvertently" make the test fail. – RubberDuck Oct 25 '16 at 23:46
  • I thought about forcing a regression bug, but given the fact that the tests I've covered so far are unit, and not integration, I don't see how to explain it to them with mere 'templates'. – Christopher Francisco Oct 25 '16 at 23:51
  • 1
    Possible duplicate of Should we test all our methods? – djechlin Oct 26 '16 at 3:54
  • 1
    Duplicate of a few questions similar to "should we unit test getters and setters" – djechlin Oct 26 '16 at 3:54
11

Tests are supposed to have a point.

Never write tests simply because you're supposed to write tests. That's ceremonial thinking. Ceremonies are an excuse to turn off your brain. It's not always better to have tests rather than just code. Sometimes it's worse.

I'm not a TDD hater. I've done it professionally. I wish I never had to work on any project that didn't have well written tests.

But I've seen mandated tests lock code in stone because the tests existed for no better reason than they were supposed to exist. Tests should have a good reason to exist.

Some tests exist to make refactoring easier.
Some tests exist to make adding features easier.
Some tests exist to make debugging easier.
Some tests exist to make conforming to a specification easier.
Some tests exist to make satisfying a customer's needs easier.
Some tests exist to make spotting unintended changes easier.
Some tests exist to make it look like you write tests, just like the cool kids.

So I ask, what is the point of this test?

In your comment:

I thought about forcing a regression bug, but given the fact that the tests I've covered so far are unit, and not integration, I don't see how to explain it to them with mere 'templates'. – Christopher Francisco

you mention regression testing as if it is something only ever done with integration tests and not unit tests. This is not true. In fact regression testing is about the only excuse I can think of to write a test like you've done here. This is a unit test. What would make it also a regression test is running it even when you don't think you've made changes to this unit.

This getFooFileContent() function has trivially simple behavior. I see no logic to test, no corner cases to explore, no potential bugs to uncover. A good testing poster boy this function aint.

A regression test is about the only point I can see here. That begs the question of why you want to regression test this.

A good story to justify a regression test is you want to be alerted if behavior changes. This story is a hard sell to time limited maintenance programmers who want changes to be easier to make not harder. So you sell the point that it makes debugging easier. If somehow the behavior changes it's spotted before you ship.

Well why would that change? I hear your team ask. Because someone changed it of course. Well what if they meant to? What's happening here is changes are becoming expensive because now I can't update this thing without updating it's test. That's a cost and it needs to be justified.

Say someone was scrolling around and without noticing they highlighted a bit of this function, then bumped the space bar and now that bit is gone. This unnoticed change manages to compile and so is about to go out the next time we ship.

Yeah, it's far fetched. But it could happen. If it did, what would it cost?

Well it might bankrupt the company. A bit hard to tell from this example. That's the real problem. You need to tell the story of the test proving it's worth.

Keep in mind that you use source control (you do right?) so nothing has been lost forever. All this is buying you is knowing about the problem sooner rather than later.

A test of trivial behavior comes at a cost. Not testing trivial behavior comes at a cost. Here is where you must find balance. You'll only find that balance when you know why you're writing the test. Make sure you know the story that would make this test useful.

You can try to test everything that could break but I'll settle for writing every test that makes the job easier.

  • Given the case where the implementation for getting the text for the template changes (say it will be read from a template file, or maybe downloaded from a repo over network), the test WILL cover regression. This answer sounds as if you're supposing the implementation will always stay a basic return "a hardcoded string" forever, rendering it unable to explain the value of the test. – Christopher Francisco Oct 26 '16 at 14:39
  • On the other hand, if you think about it, said test do cover: refactoring easier, conforming to an specification, spotting unintended changes easier. – Christopher Francisco Oct 26 '16 at 14:42
  • 1
    I actually tried very hard not the suppose much. All I know is it's returning a hard coded string now. I don't know how or if it will change. More to the point I don't know what it is used for. Is this text for a nearly inconsequential tool tip? Is this your freaking user license agreement? The importance of a test goes well beyond the structure of the code. That's why I'm putting the question of weighing the costs back on you. – candied_orange Oct 26 '16 at 15:06
3

If I were testing a template project generator, then I would not write a test for that getter; what I would do is write a test which runs the entire generator and compares the files it created to a collection of static files which is the expected output. This is entirely sufficient testing for all parts of the generator which have no interesting logic. In cases where there is interesting logic, presumably there will be some kind of input parameters and you will want to write unit tests covering the various parameters, which will not have your “simply repeating the code under test” problem.

Some would argue that this is not a unit test, but an integration test. Well, fair enough, but my perspective is that it is more important that the entire system is usefully tested than that tests of a specific classification exist. Let's consider the advantages of a unit test over an integration test:

  1. The test involves less setup and is therefore much faster.
  2. It is easier to find the cause of a failure because there is less code under test that could have caused it.
  3. It is easier (or possible at all) to test edge cases because you don't have to find a configuration of or input to the entire system which exercises the particular case in a particular class.

In this case, I'm assuming that (1) is unlikely to be a problem, (3) does not apply because the code is trivial, and (2) does not apply because the source of the incorrect string is obvious — if you don't already know that getFooFileContent is involved, just searching the code for the incorrect output string will find the source string literal.

And by doing it this way, refactorings of how the foo file's content is generated that remove/relocate that method don't cause you to have to change the tests, which means that it is easy to see that the change is a pure refactoring since the tests passed before, passed after, and were not modified.

  • This makes sense. Of course the actual template generator take input parameter on certain methods, and it has dependencies of its own. I agree in the fact that an integration test would cover the whole scenario and it's not that difficult to find the cause of a regression error if implementatien were to change. I did, however, chose to unit test in this case, since I'm introducing testing to my team; so that they have kind of a class they know and they can use its test as a guide. – Christopher Francisco Oct 27 '16 at 13:44
  • 1
    @ChristopherFrancisco My concern is the same as the other answerer: you are trying to set a good example, but potentially setting a bad one by following a rule that is too uncompromising. – Kevin Reid Oct 27 '16 at 14:17
  • Ok I think understand now. I'm acting under the assumption that this kind of test is correct; but the fact is this kind of test is incorrect. If that's the case, then could you list some cases where unit testing is appropriate instead of an integration test? – Christopher Francisco Oct 27 '16 at 14:46
  • @ChristopherFrancisco I think that's getting to be a different question, really. But I already had such a list in my answer, and I just added to it a third reason to write unit tests (which again does not apply in this case). – Kevin Reid Oct 27 '16 at 15:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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