Question

I am trying to learn Java and at the same time implement test driven development (TDD) methodology. I have some experience in object oriented programming concepts to understand Java but not with TDD. I wrote a very simple Java program along with unit tests but I came across some questions while solving the problem. I thought that someone here on SO might be able to clarify my questions. I have listed the problem, my final unit test class, class implementation logic, the approach I took to solve the problem and the questions that I have.

Problem:

I took the first problem SleepIn listed on CodingBat website to try and implement simple unit tests. Here is the problem as stated on the website.

The parameter weekday is true if it is a weekday, and the parameter vacation is true if we are on vacation. We sleep in if it is not a weekday or we're on vacation. Return true if we sleep in.

Unit tests:

I wrote the unit test class and created four methods to test the outcome of the possible inputs available for the above stated question.

package com.codingbat.practice.unit;

import static org.junit.Assert.*;
import org.junit.Test;
import com.codingbat.practice.SleepIn;

public class SleepInTest {

    @Test
    public void weekend_and_not_vacation() {
        SleepIn sleepIn = new SleepIn();
        sleepIn.setWeekday(false);
        sleepIn.setVacation(false);
        assertTrue(sleepIn.allow());
    }

    @Test
    public void weekend_and_vacation() {
        SleepIn sleepIn = new SleepIn();
        sleepIn.setWeekday(false);
        sleepIn.setVacation(true);
        assertTrue(sleepIn.allow());
    }

    @Test
    public void weekday_and_not_vacation() {
        SleepIn sleepIn = new SleepIn();
        sleepIn.setWeekday(true);
        sleepIn.setVacation(false);
        assertFalse(sleepIn.allow());
    }

    @Test
    public void weekday_and_vacation() {
        SleepIn sleepIn = new SleepIn();
        sleepIn.setWeekday(true);
        sleepIn.setVacation(true);
        assertTrue(sleepIn.allow());
    }
}

Class implementation:

Here is the class implementation that solves the above stated problem.

package com.codingbat.practice;

public class SleepIn {
    boolean weekday;
    boolean vacation;

    public SleepIn() {
    }

    public void setWeekday(boolean weekday) {
        this.weekday = weekday;
    }

    public void setVacation(boolean vacation) {
        this.vacation = vacation;
    }

    public boolean allow() {
        return (!this.weekday || this.vacation);
    }
}

Approach that I took:

  1. Wrote the method SleepIn.allow() to simply return false.

  2. Implemented the four unit tests and passed the appropriate arguments and used the necessary assert methods based on what I thought should be the outcome of the class implementation.

  3. Executed the test cases. 3 of them failed and 1 succeeded.

  4. Test weekday_and_not_vacation() succeeded because it expects false and the implementation currently returns only false.

  5. Implemented the necessary logic in method SleepIn.allow() to solve the problem.

  6. Executed the tests again. All 4 tests passed.

  7. Replaced the || with && in the implementation logic to break the solution and ran the tests again to verify that at least one of the tests failed. 2 out of 4 tests failed. Put the correct logic back into the method again to verify that all the tests succeeded.

Questions:

  1. My understanding of TDD is to make the tests fail before writing the implementation logic. Here in this case, the method returns a boolean. Hence, I could only return either true or false. I couldn't make all the tests fail to begin with. Does the recommended TDD approach match with the steps I took to solve the problem?

  2. Reading about TDD on various websites, I inferred that each unit test should be independent of each other. Based on that, I created an instance of SleepIn in every unit test to keep all the testing logic confined to each unit test method. Is that how unit tests are usually implemented?

  3. In the implementation class, I could have created the constructor with two parameters to instantiate the instance variables. However, I created the setters because it seems more meaningful to call the method names to pass the values. I understand that the instance variables in this case would contain false if the appropriate setters are not called. What is the recommended approach in Java or in general Object Oriented programming implementations? I did not implement getters since I didn't feel the need for this problem.

  4. I assume that it is necessary to know what the outcome would be for any given input when writing unit tests so that we can use the appropriate asserts. Here in this case, the inputs are two booleans and I know what the outputs are since the problem is very simple. So, I chose the appropriate assertTrue or assertFalse accordingly. Is this how the unit tests are supposed to be written? How would it scale when the logic becomes complicated, for example, in real world applications? If it gets complicated, does it mean the problem in question should be broken down and kept as small as possible so we can easily test with unit tests?

Was it helpful?

Solution

First, to address your questions individually:

1.My understanding of TDD is to make the tests fail before writing the implementation logic. Here in this case, the method returns a boolean. Hence, I could only return either true or false. I couldn't make all the tests fail to begin with. Does the recommended TDD approach match with the steps I took to solve the problem?

TDD requires humbly suggests that you write your tests one at a time, implementing the code necessary to pass each test before moving on to the next.

Not only does this cut down on the amount of code that is written at any given time, it actually influences design choices as well. Listen to your tests.

2.Reading about TDD on various websites, I inferred that each unit test should be independent of each other. Based on that, I created an instance of SleepIn in every unit test to keep all the testing logic confined to each unit test method. Is that how unit tests are usually implemented?

This is a common way of handling the SUT in a unit test. The main thing is to minimize/avoid sharing resources (e.g., an instance of a class) between tests. If state is shared between tests, it becomes much harder to write them. Listen to your tests.

3.In the implementation class, I could have created the constructor with two parameters to instantiate the instance variables. However, I created the setters because it seems more meaningful to call the method names to pass the values. I understand that the instance variables in this case would contain false if the appropriate setters are not called. What is the recommended approach in Java or in general Object Oriented programming implementations? I did not implement getters since I didn't feel the need for this problem.

There is no general rule-of-thumb for this; if something needs to configure a parameter via a setter then there's a driving force for one existing. However, TDD asks that you only write as much code as necessary to pass the test. Are the setters necessary for passing this test? As you mentioned, there are other ways of getting these parameters into the SUT. Listen to your tests.

4.I assume that it is necessary to know what the outcome would be for any given input when writing unit tests so that we can use the appropriate asserts. Here in this case, the inputs are two booleans and I know what the outputs are since the problem is very simple. So, I chose the appropriate assertTrue or assertFalse accordingly. Is this how the unit tests are supposed to be written? How would it scale when the logic becomes complicated, for example, in real world applications? If it gets complicated, does it mean the problem in question should be broken down and kept as small as possible so we can easily test with unit tests?

It's absolutely imperative that your tests are deterministic. (If they weren't, how would you know that they were reliable?) A test needs to have complete control over the context in which the SUT operates.

If the SUT needs to generate a random number, the test should supply a dummy object to the SUT that generates a not-so-random number.

If the SUT needs to ask the system clock for the current time, the test should supply an object that is effectively frozen in time.

If a test starts to become too complex to maintain, it's trying to tell you something.

Listen to your tests.

...[W]hen code is difficult to test, the most likely cause is that our design needs improving. The same structure that makes the code difficult to test now will make it difficult to change in the future. By the time that future comes around, a change will be more difficult still because we'll have forgotten what we were thinking when we wrote the code.

The thing about TDD is that it's not actually focused on testing, but rather designing a system to be robust and modular. Testing just happen to be one of the easiest ways of verifying such a design (i.e., tests are hard to write, unless your system is robust and modular).

By requiring us to write tests before we write any corresponding code, TDD essentially forces us to codify our requirements. That is, unit tests are the embodiment -in code- of a system's requirements.

With this in mind, it's helpful to write a test from the perspective of a requirement. ("Is there a requirement for this? If not, then why am I writing all this code?")

For example, the webpage for your CodingBat problem seems to imply a requirement that a method exist with the following signature:

public boolean sleepIn(boolean weekday, boolean vacation) {
    // TODO
}

From this premise, we can better answer your question #3 - instead of passing the parameters in a setter, or in the constructor of the class, we could just pass them into the function itself.

Now let's factor in the stated requirements of the problem:

  1. Allow us to sleep in if it is the weekend
  2. Allow us to sleep in if we are on vacation

We should be able to map each of these requirements to an individual unit test. Imagine we codify requirement #1 in a unit test can_sleep_in_on_weekend. What's the least amount of code we could write to accomplish this? Maybe:

package com.codingbat.practice;

public class SleepIn {

    public SleepIn() {
    }

    public boolean sleepIn(boolean weekday, boolean vacation) {
        return !weekday;
    }
}

Moving on to the next requirement, say we codify it in the unit test can_sleep_in_during_vacation. What is the least amount of code to pass this test?

package com.codingbat.practice;

public class SleepIn {

    public SleepIn() {
    }

    public boolean sleepIn(boolean weekday, boolean vacation) {
        return !weekday || vacation;
    }
}

As there are no more requirements after this, we're done writing code.

OTHER TIPS

In standard TDD, you should only write one test at a time, then make that test pass. Start with the normal case, say weekday_and_not_vacation. See it fail, because you are returning true. Return false, and watch it pass. Now take not_weekday_and_not_vacation. See it fail, because you are returning false. Put in logic that checks weekday; now both tests pass. Now weekday_and_vacation, lather, rinse, repeat.

  1. Because you wrote all the tests first, you wound up with some passing. That's less likely when you write, and make pass, one test at a time.
  2. It's quite reasonable to create a new SleepIn instance for each test. Not necessary, but reasonable and in no way against standard TDD practice.
  3. Setters or parameters are fine; whatever makes most sense in the context of your use of the class.
  4. Yes, you should always be able to know in advance what the expected outcome is. If your problem is too complicated, break it down until you can say what the next part your testing should do. That's a key part of where Design comes into play in Test-Driven Design.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top