2 분 소요


Why Write Test Code

  • Documentation of code
  • To discover defects in code
  • To ensure stability during refactoring

What is TDD?

  • Test Driven Development
  • A development method where test code is written before production code
  • TFD (Test First Development) + Refactoring
  • Verify functionality first (at the method level)

What about BDD?

  • Behavior Driven Development
  • A method of writing test code based on scenarios
  • Each scenario follows a Given / When / Then structure

Password Validator

Requirements and Specifications

  • Password must be at least 9 characters and no more than 15 characters
  • Throw an Exception if password is less than 9 or more than 15 characters
  • Verify boundary conditions

Dependency

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
    testImplementation 'org.assertj:assertj-core:3.23.1'
}

test {
    useJUnitPlatform()
}

Writing Password Length Validation Test Code

Creating Test Class and Basic Class

  • It’s best if the test code package location matches the main source package location
  • First implement requirements in test code, then gradually create parts in actual code that haven’t been implemented yet
package org.example;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatCode;

/**
 * Password must be at least 9 characters and no more than 15 characters
 * Throw Exception if password is less than 9 or more than 15 characters
 * Verify boundary conditions
 */
public class PasswordValidatorTest {

    @DisplayName("Password is valid if at least 9 and at most 15 characters") // Test intent
    @Test
    void validatePasswordTest() {
        assertThatCode(() -> PasswordValidator.validate("123456789"))
                .doesNotThrowAnyException();
    }
}
  • After writing, a red line appears for the PasswordValidator class
  • Create PasswordValidator in main source
  • Create validate method in PasswordValidator class
      package org.example;
      public class PasswordValidator {
          public static void validate(String password) {
          }
      }
    
  • Run test -> Success

Refactoring Created Classes to Reflect Detailed Conditions

  • Check if the input string is at least 9 and at most 15 characters, throw Exception if not
package org.example;

public class PasswordValidator {

    public static final String WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE = "Password must be at least 9 and at most 15 characters.";

    public static void validate(String password) {
        int length = password.length();

        if (length < 9 || length > 15) {
            throw new IllegalArgumentException(WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
        }

    }
}
  • Run test -> Success

Testing When Password is Less Than 9 Characters

  • Using the same approach: try in TestCode first, then implement in actual code one by one
  • Since Exception handling is already written, there’s not much to modify - just test the condition
public class PasswordValidatorTest {
    @DisplayName("Exception thrown when password is less than 9 characters")
    @Test
    void validatePasswordShortExceptionTest() {
        assertThatCode(() -> PasswordValidator.validate("1234"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
    }
}

Testing When Password Exceeds 15 Characters

  • Using the same approach: try in TestCode first, then implement in actual code one by one
  • Since Exception handling is already written, there’s not much to modify - just test the condition

public class PasswordValidatorTest {
    @DisplayName("Exception thrown when password exceeds 15 characters")
    @Test
    void validatePasswordLongExceptionTest() {
        assertThatCode(() -> PasswordValidator.validate("1234512345123456"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);
    }
}

Testing Boundary Conditions

  • The password length that meets our requirements is at least 9 and at most 15 characters
  • The boundary values for this condition are passwords of 8 or 16 characters
  • Adding tests for boundary values helps create good tests
  • Let’s test using @Parameterize

Adding Dependency

testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'

Testing with Parameterize

public class PasswordValidatorTest {

    @DisplayName("Test boundary conditions")
    @ParameterizedTest
    @ValueSource(strings = {"12345678", "1234567890123456"})
    void validatePasswordBoundaryTest(String password) {
        assertThatCode(() -> PasswordValidator.validate(password))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining(PasswordValidator.WRONG_PASSWORD_LENGTH_EXCEPTION_MESSAGE);

    }

}
  • Values inside ValueSource are automatically cycled through during execution
  • Data declared in ValueSource can be received one by one in the function
  • When executed, unlike other tests, this test runs twice

Result Screen

Test Result Screen

댓글남기기