Skip to main content

Command Palette

Search for a command to run...

Code Smell 82 - Tests Violating Encapsulation

Published
2 min read
Code Smell 82 - Tests Violating Encapsulation
M

I’m a senior software engineer loving clean code, and declarative designs. S.O.L.I.D. and agile methodologies fan.

Objects work fine and fulfill business objectives. But we need to test them. Let's break them.

TL;DR: Don't write methods with the only purpose of being used in your tests.

Problems

  • Encapsulation Violation.

  • Bad interfaces

  • Coupling

Solutions

  1. Don't break encapsulation.

  2. Test must be in full control.

  3. If you cannot control your object, you are coupled. Decouple them!

Sample Code

Wrong

<?

class Hangman {
    private $wordToGuess;

    function __construct() {
        $this->wordToGuess = getRandomWord();
        //Test is not in control of this
    }

    public function getWordToGuess(): string {
        return $this->wordToGuess;
        //Sadly we need to reveal this
    }
}

class HangmanTest extends TestCase {
    function test01WordIsGuessed() {
        $hangmanGame = new Hangman();
        $this->assertEquals('tests', $hangmanGame->wordToGuess());
        //how can we make sure the word is guessed?
    }
}
<?

class Hangman {
    private $wordToGuess;

    function __construct(WordRandomizer $wordRandomizer) {
        $this->wordToGuess = $wordRandomizer->newRandomWord();
    }
}

class MockRandomizer implements WordRandomizer {
    function newRandomWord(): string {
        return 'tests';
    }
}

class HangmanTest extends TestCase {
    function test01WordIsGuessed() {
        $hangmanGame = new Hangman(new MockRandomizer());
        //We are in full control!
        $this->assertFalse($hangmanGame->wordWasGuessed());
        $hangmanGame->play('t');
        $this->assertFalse($hangmanGame->wordWasGuessed());
        $hangmanGame->play('e');
        $this->assertFalse($hangmanGame->wordWasGuessed());
        $hangmanGame->play('s');
        $this->assertTrue($hangmanGame->wordWasGuessed());
        //We just test behavior
    }
}

Detection

This is a design smell.

We can detect we need a method just for test.

Tags

  • Information Hiding

Conclusion

White-box tests are fragile. They test implementation instead of behavior.

Relations

More Info

Credits

This smell was inspired by Rodrigo


Nothing makes a system more flexible than a suite of tests.

Robert Martin


This article is part of the CodeSmell Series.

R
Rodrigo4y ago

Great example Maxi! it's exactly how I imagined it

1

Code Smells

Part 1 of 50

In this series, we will see several symptoms and situations that make us doubt the quality of our developments. We will present possible solutions. Most are just clues. They are no hard rules.