SOLID Unit Tests

Set of Unit Tests for the previous SOLID Examples post.

Single Responsibility Principle (SRP)

Unit tests for the Authenticator class from the Single Responsibility Principle example from the previous post.

use PHPUnit\Framework\TestCase;

class AuthenticatorTest extends TestCase
{
  public function testAuthenticateWithValidCredentials()
  {
      // Arrange
      $credentialsValidator = $this->createMock(CredentialsValidator::class);
      $credentialsValidator->method('isValid')
          ->willReturn(true);
      $sessionGenerator = $this->createMock(SessionGenerator::class);
      $sessionGenerator->method('generate')
          ->willReturn('abcdef123456');
      $cookieManager = $this->createMock(CookieManager::class);
      $cookieManager->expects($this->once())
          ->method('set')
          ->with('sessionToken', 'abcdef123456');
      $redirector = $this->createMock(Redirector::class);
      $redirector->expects($this->once())
          ->method('redirect')
          ->with('/dashboard');
      $authenticator = new Authenticator(
          $credentialsValidator,
          $sessionGenerator,
          $cookieManager,
          $redirector
      );
      // Act
      $authenticator->authenticate('user', 'pass');

      // Assert
      // Ensure that the user is redirected to the dashboard
  }
  public function testAuthenticateWithInvalidCredentials()
  {
    // Arrange
    $credentialsValidator = $this->createMock(CredentialsValidator::class);
    $credentialsValidator->method('isValid')->willReturn(false);
    $sessionGenerator = $this->createMock(SessionGenerator::class);
    $cookieManager = $this->createMock(CookieManager::class);
    $redirector = $this->createMock(Redirector::class);
    $redirector->expects($this->once())
      ->method('redirect')
      ->with('/login?error=invalid_credentials');
    $authenticator = new Authenticator(
      $credentialsValidator,
      $sessionGenerator,
      $cookieManager,
      $redirector
    );

    // Act
    $authenticator->authenticate('user', 'pass');
    // Assert
    // Ensure that the user is redirected to the login page with an error message
  }
}

These tests use PHPUnit to create mock objects for the CredentialsValidator, SessionGenerator, CookieManager, and Redirector classes. The testAuthenticateWithValidCredentials() test ensures that a user is redirected to the dashboard when they enter valid login credentials, while the testAuthenticateWithInvalidCredentials() test ensures that a user is redirected to the login page with an error message when they enter invalid login credentials.

These tests cover the expected behaviour of the Authenticator class and help to ensure that it adheres to the Single Responsibility Principle.

Open/Closed Principle (OCP)

Unit tests for the VendorDiscountShoppingCart class from the Development Notes Solid Examples post for Open/Closed Principle using PHPUnit.

use PHPUnit\Framework\TestCase;

class VendorDiscountShoppingCartTest extends TestCase
{
    public function testGetTotalWithoutDiscount()
    {
        // Arrange
        $item1 = new Item('item1', 10.00, 1, 1);
        $item2 = new Item('item2', 20.00, 1, 2);
        $shoppingCart = new ShoppingCart();
        $shoppingCart->addItem($item1);
        $shoppingCart->addItem($item2);
        $vendorDiscountShoppingCart = new VendorDiscountShoppingCart($shoppingCart);

        // Act
        $total = $vendorDiscountShoppingCart->getTotal(1);

        // Assert
        $this->assertEquals(10.00, $total);
    }

    public function testGetTotalWithDiscount()
    {
        // Arrange
        $item1 = new Item('item1', 10.00, 1, 1);
        $item2 = new Item('item2', 10.00, 1, 1);
        $item3 = new Item('item3', 10.00, 1, 1);
        $item4 = new Item('item4', 10.00, 1, 1);
        $item5 = new Item('item5', 10.00, 1, 1);
        $shoppingCart = new ShoppingCart();
        $shoppingCart->addItem($item1);
        $shoppingCart->addItem($item2);
        $shoppingCart->addItem($item3);
        $shoppingCart->addItem($item4);
        $shoppingCart->addItem($item5);
        $vendorDiscountShoppingCart = new VendorDiscountShoppingCart($shoppingCart);

        // Act
        $total = $vendorDiscountShoppingCart->getTotal(1);

        // Assert
        $this->assertEquals(45.00, $total);
    }
}

These tests use PHPUnit to create Item objects and add them to a ShoppingCart, which is passed to a VendorDiscountShoppingCart object. The testGetTotalWithoutDiscount() test ensures that the getTotal() method returns the correct total without a discount, while the testGetTotalWithDiscount() test ensures that the getTotal() method returns the correct total with a discount. These tests cover the expected behaviour of the VendorDiscountShoppingCart class and help to ensure that it adheres to the Open/Closed Principle.

Liskov Substitution Principle (LSP)

A set of unit tests for the LibraryItem class hierarchy from the Development Notes Solid Examples post for Liskov Substitution Principle (LSP) example using PHPUnit.

use PHPUnit\Framework\TestCase;

class LibraryItemTest extends TestCase
{
    public function testBookGetDuration()
    {
        // Arrange
        $book = new Book();

        // Act
        $duration = $book->getDuration();

        // Assert
        $this->assertEquals(0, $duration);
    }

    public function testDVDGetDuration()
    {
        // Arrange
        $dvd = new DVD();
        $dvd->setDuration(90);

        // Act
        $duration = $dvd->getDuration();

        // Assert
        $this->assertEquals(90, $duration);
    }

    public function testAudioBookGetDuration()
    {
        // Arrange
        $audioBook = new AudioBook();

        // Act
        $duration = $audioBook->getDuration();

        // Assert
        $this->assertEquals(0, $duration);
    }
}

These tests use PHPUnit to create instances of the Book, DVD, and AudioBook classes, and call the getDuration() method on each object. The testBookGetDuration() test ensures that the getDuration() method returns 0 for a Book object, the testDVDGetDuration() test ensures that the getDuration() method returns the correct duration for a DVD object, and the testAudioBookGetDuration() test ensures that the getDuration() method returns 0 for an AudioBook object. These tests cover the expected behaviour of the LibraryItem class hierarchy and help to ensure that it adheres to the Liskov Substitution Principle (LSP).

Interface Segregation Principle (ISP)

Unit tests for the TextEditor and ImageEditor interfaces from the Interface Segregation Principle (ISP) example from SOLID Examples post using PHPUnit.

use PHPUnit\Framework\TestCase;

class TextEditorTest extends TestCase
{
    public function testEditText()
    {
        // Arrange
        $textContent = new TextContent('Hello, world!');
        $textEditor = new SimpleTextEditor();

        // Act
        $textEditor->editText($textContent);

        // Assert
        $this->assertEquals('Hello, world!', $textContent->getText());
    }
}

class ImageEditorTest extends TestCase
{
    public function testUploadImage()
    {
        // Arrange
        $imageContent = new ImageContent('example.jpg');
        $imageEditor = new ImageUploader();
        $file = 'example.jpg';

        // Act
        $imageEditor->uploadImage($imageContent, $file);

        // Assert
        $this->assertEquals('example.jpg', $imageContent->getImage());
    }
}

These tests use PHPUnit to create instances of the TextContent and ImageContent classes, and instances of the SimpleTextEditor and ImageUploader classes that implement the TextEditor and ImageEditor interfaces, respectively. The testEditText() test ensures that the editText() method of the SimpleTextEditor class edits the text content of a TextContent object as expected, while the testUploadImage() test ensures that the uploadImage() method of the ImageUploader class uploads a new image file to an ImageContent object as expected. These tests cover the expected behaviour of the TextEditor and ImageEditor interfaces and help to ensure that they adhere to the Interface Segregation Principle (ISP).

Dependency Inversion Principle (DIP)

An example set of unit tests for the Mailer class and its dependencies from the Dependency Inversion Principle (DIP) example from the SOLID Examples post using PHPUnit.

use PHPUnit\Framework\TestCase;

class MailerTest extends TestCase
{
    public function testSendEmailWithSmtpTransport()
    {
        // Arrange
        $transport = $this->createMock(MailerTransport::class);
        $transport->expects($this->once())
            ->method('send')
            ->with('user@example.com', 'Welcome!', 'Thanks for signing up.');
        $container = new Container();
        $container->bind(MailerTransport::class, SmtpTransport::class);
        $mailer = $container->make(Mailer::class, [$transport]);

        // Act
        $mailer->sendEmail('user@example.com', 'Welcome!', 'Thanks for signing up.');
    }

    public function testSendEmailWithMailgunTransport()
    {
        // Arrange
        $transport = $this->createMock(MailerTransport::class);
        $transport->expects($this->once())
            ->method('send')
            ->with('user@example.com', 'Welcome!', 'Thanks for signing up.');
        $container = new Container();
        $container->bind(MailerTransport::class, MailgunTransport::class);
        $mailer = $container->make(Mailer::class, [$transport]);

        // Act
        $mailer->sendEmail('user@example.com', 'Welcome!', 'Thanks for signing up.');
    }
}

These tests use PHPUnit to create mock objects for the MailerTransport interface and the Container class. The testSendEmailWithSmtpTransport() test ensures that the Mailer class sends an email using the SmtpTransport implementation of the MailerTransport interface, while the testSendEmailWithMailgunTransport() test ensures that the Mailer class sends an email using the MailgunTransport implementation of the MailerTransport interface. These tests cover the expected behaviour of the Mailer class and its dependencies and help to ensure that it adheres to the Dependency Inversion Principle (DIP).

Leave a Reply