테스트 환경을 구성한다고 가정해 보자.

테스트 환경에서 필요한 값들을 추상화해 놓은 TestEnvironment 가 있다.

trait TestEnvironment { 
  val envName: String 
  def readEnvironmentProperties: Map[String, String] 
}

아래 코드는 TestEnvironment 의 값들을 이용해 테스트를 실행하는 클래스다.

class TestExecutor { env: TestEnvironment =>  // TestEnvironment 의 구현체를 직접 주입해 주어야 한다.
  def execute(tests: List[Test]): Boolean = { 
    println(s"Executing test with $envName environment")
    tests.forall(_.execute(readEnvironmentProperties))
  } 
}

위 코드에서 env: TestEnvironment 를 다른 곳으로 옮겨보자.

// TestEnvironment 를 관리하는 Component
trait TestEnvironmentComponent { 
  val env: TestEnvironment // env 는 변수로 사용될 수 있다. 어디선가 override 함으로써 갈아낄 수 있다.
  trait TestEnvironment { 
    val envName: String 
    def readEnvironmentProperties: Map[String, String] 
  } 
}

TestExecutor 가 TestExecutorComponent 내부로 들어갔다.

TestExecutor 는 더이상 TestEnvironment 에 의존하지 않는다.

대신, TestEnvironment 를 env 라는 변수로 사용할 수 있게 되었다.

trait TestExecutorComponent { this: TestEnvironmentComponent => 
  val testExecutor: TestExecutor 
  class TestExecutor { 
    def execute(tests: List[Test]): Boolean = { 
      println(s"Executing test with ${env.envName} environment")
      tests.forall(_.execute(env.readEnvironmentProperties)) 
    } 
  } 
}

Component 들간의 의존 관계를 self-type 인 this 로 정할 수 있다.

만약 TestExecutorComponent 가 의존하는 컴포넌트에 LoggingComponent 가 추가된다면

this: TestEnvironmentComponent with LoggingComponent

처럼 할 수 있다.