0

I have a class

class A {
  List<int> a;
  A() {
    this.a = [];
  }
  void add(int x) {
    a.append(x)
  }
  List<int> display() {
     return a;
  }
}

This is a simple class I want to write in a TDD approach. my question is how do you write the test for the display() since the add() need to be called to make the test useful.

And it feels wrong to test the function it needs to rely on the other function which requires testing.

How to we handle this?

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 1
    Does this answer your question? [Unit testing functions calling other tested functions](https://softwareengineering.stackexchange.com/questions/287546/unit-testing-functions-calling-other-tested-functions) – gnat Dec 23 '20 at 19:30
  • 1
    Your question makes no sense. Since you are using TDD, that means you are *only allowed to write code to make a failing test pass*. That means, the *only way* how `display` can exist *at all* is that you *already* have test for it. So, if you already have a test for it, then why are you asking how to write a test for it? – Jörg W Mittag Dec 24 '20 at 12:40
  • I am not writing the TDD and asking the question, clearly i want to write the about class with TDD approach and with TDD i want to be able to test all the function independently but to test the function independent i require the new features such as parameterized constructor which i dont want to implement since its not required for the business. – rahul Kushwaha Dec 24 '20 at 13:56
  • @JörgWMittag rigorously following the red green refactor cycle still allows you to write this code and still allows you to wonder about depending on other functions. – candied_orange Dec 24 '20 at 16:34

2 Answers2

3

TDD doesn’t say you can only call one method. You have setup to do. That’s all. Now sure it means the function under test isn’t the only thing that can cause the test to fail. But that’s true whenever object state is involved. It has to be set by something.

Don’t get so hung up on the structure of you code. When a test fails just be sure it’s not hard to find the failure.

If your state was constant and set by an initializer, rather than constructor or setter, its code would be just as involved.

Remember, the point is to test the interface. Not to lock the class down to any particular structure.

I see at least two tests here. One involves constructor, setter, and getter. One only involves constructor and getter. But that one has to know the default value. Up to you if you really want to lock that down.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • so, you are saying that it's ok to use the other function in combination in a single test. here in a test I will call `add()` to add the data and verify the test by calling the `display()` and vice versa. – rahul Kushwaha Dec 24 '20 at 04:29
  • It can’t be avoided. You have to supply dependencies somehow. They are a problem when they are non-deterministic or slow. But when you need them you need them. – candied_orange Dec 24 '20 at 05:29
2

I think this is best understood by going through your example (in pseudocode):

  • let us assume class A already exists, by prior work (maybe TDD or not, does not matter), but it does not have add, display and the internal member a.

  • First test:

    TestAfterConstructionReturnedListIsEmpty()
    {
         var a = new A();
         Assert.AreEqual(0, a.display().size());
    }
    

    Now this fails at compilation, since display does not exist. So the next step is to implement the display method. One could go and implement List<int> a; right here, but for demonstration purposes, let us do this in baby steps:

    List<int> display() { return [];}
    

    will make the test pass. There is nothing much to refactor at this stage, so let us continue.

  • Second test:

    TestAddingOneElementThisElementFormsTheList()
    {
       var a = new A();
       a.add(123);
       var result = a.display();
       Assert.AreEqual([123],result.display());
    }
    

    Now, you first have to add the add method to make this compile (an empty implementation will be enough). Then, the test will run, but still fail. So next step is add the minimal amount of code this test will not fail any more (but without making any assumptions of what data is passed in the tests, of course.)

    A very simple implementation could look like this

     class A
     {
        int a;
         void add(int x) {a=x;}
         List<int> display(){return [a];}
     }
    

    But then you will notice that though the second test passes now, the first one will fail.

Hence, at this stage, you will probably add List<int> a to A and complete the implementations of add and display in your code the way scetched in the question, since I can hardly imagine a simpler implementation to fulfill the requirements of both existing tests.

Note TDD does not mean to write one test after another to add one function after another to a class. It is about writing one test after another to add some additional behaviour to a class. This behaviour may involve adding or changing functions or also touching several existing ones.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565