You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Playframework 2.6与Macwire编译时依赖注入配置与测试咨询

Hey there! Let's walk through getting your Play 2.6 + MacWire compile-time DI setup working properly, along with testing strategies tailored to your project structure.

1. Complete the Compile-Time DI Configuration

1.1 Finish the DefyProductsComponents Class

Your existing component class needs to extend the right base components and wire up all dependencies using MacWire. Here's the full implementation:

package common

import com.softwaremill.macwire.wire
import controllers.{Assets, AssetsComponents, HomeController}
import play.api.ApplicationLoader.Context
import play.api.BuiltInComponentsFromContext
import play.api.routing.Router
import play.filters.HttpFiltersComponents
import router.Routes
import scala.concurrent.ExecutionContext

class DefyProductsComponents(context: Context) 
  extends BuiltInComponentsFromContext(context)
  with HttpFiltersComponents  // Includes default HTTP filters
  with AssetsComponents {     // Provides Assets controller dependencies

  // Explicitly provide ExecutionContext (reused from Play's default)
  implicit override lazy val executionContext: ExecutionContext = actorSystem.dispatcher

  // Wire up your custom controllers
  lazy val homeController: HomeController = wire[HomeController]

  // Build the router using the wired controllers
  override lazy val router: Router = {
    val prefix = "/"
    wire[Routes].setPrefix(prefix)
  }
}

Key Notes:

  • We extend BuiltInComponentsFromContext to get core Play components (like actorSystem, configuration), plus HttpFiltersComponents and AssetsComponents to cover filter and asset handling.
  • wire[HomeController] tells MacWire to automatically resolve all constructor dependencies for your controller (make sure your HomeController's constructor accepts dependencies that are available in the components class).
  • The router is wired using Play's generated Routes class, with the default root prefix set.

1.2 Implement the DefyProductsLoader

Next, complete your application loader to use the components class:

package common

import play.api.ApplicationLoader
import play.api.ApplicationLoader.Context

class DefyProductsLoader extends ApplicationLoader {
  override def load(context: Context) = {
    new DefyProductsComponents(context).application
  }
}

1.3 Configure Play to Use Your Custom Loader

Add this line to your conf/application.conf to tell Play to use your loader instead of the default Guice one:

play.application.loader = common.DefyProductsLoader
2. Testing with Compile-Time DI (MacWire)

Testing with compile-time DI avoids runtime injection surprises—you can either test individual components directly or spin up a full application stack with your components.

2.1 Component-Level Unit Tests

For testing controllers or services in isolation, wire them directly with mocked dependencies. Let's say your HomeController depends on a ProductService:

Example HomeController (for reference):

package controllers

import play.api.mvc._
import common.ProductService

class HomeController(val controllerComponents: ControllerComponents, productService: ProductService) 
  extends BaseController {
  // Example action
  def index() = Action {
    Ok("Hello from HomeController")
  }
}

Unit Test for HomeController:

package controllers

import org.scalatestplus.play.PlaySpec
import play.api.test.FakeRequest
import play.api.test.Helpers._
import com.softwaremill.macwire.wire
import common.MockProductService  // Your custom mocked service

class HomeControllerSpec extends PlaySpec {

  // Mock dependencies
  private val mockProductService = new MockProductService()
  private val controllerComponents = stubControllerComponents()  // Play's pre-built stub for controller dependencies

  // Wire the controller with mocked dependencies
  private val homeController = wire[HomeController]

  "HomeController" should {
    "return 200 for GET /" in {
      val result = homeController.index().apply(FakeRequest(GET, "/"))
      status(result) mustBe OK
    }
  }
}

2.2 Full Application Integration Tests

If you need to test the full application stack (routes, filters, etc.), create your components instance and start the app using Play's test helpers:

package integration

import org.scalatestplus.play.{OneAppPerTest, PlaySpec}
import play.api.Application
import play.api.test.FakeRequest
import play.api.test.Helpers._
import common.DefyProductsComponents
import play.api.ApplicationLoader.Context
import play.api.test.DefaultTestContext

class HomeControllerIntegrationSpec extends PlaySpec with OneAppPerTest {

  // Override app creation to use our components instead of Guice
  override def app: Application = {
    val testContext = Context(DefaultTestContext())
    new DefyProductsComponents(testContext).application
  }

  "HomeController integration test" should {
    "render the index page successfully" in {
      val request = FakeRequest(GET, "/")
      val homeResponse = route(app, request).get

      status(homeResponse) mustBe OK
      contentType(homeResponse) mustBe Some("text/html")
    }
  }
}

Key Testing Tips:

  • Use Play's stubControllerComponents() for mocking controller-related dependencies in lightweight unit tests.
  • For integration tests, use DefaultTestContext() to create a test-friendly application context that avoids production configuration.
  • MacWire will throw compile errors if dependencies are missing—this is a feature! It catches DI issues early, before runtime.

内容的提问来源于stack exchange,提问作者agusgambina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:44:57