如何将 ScalaTest 与 Spring 集成

发布于 2024-11-14 12:24:52 字数 864 浏览 4 评论 0原文

我需要使用 Spring 上下文中的 @Autowired 字段填充我的 ScalaTest 测试,但大多数 Scalatest 测试(例如 FeatureSpec)无法由 SpringJUnit4ClassRunner 运行。 class -

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="myPackage.UnitTestSpringConfiguration", loader=AnnotationConfigContextLoader.class)
public class AdminLoginTest {
    @Autowired private WebApplication app;
    @Autowired private SiteDAO siteDAO;

(Java,但您明白要点了)

如何从 ScalaTest 的 ApplicationContext 填充 @Autowired 字段?

class AdminLoginFeatureTest extends FeatureSpec with GivenWhenThen with ShouldMatchersForJUnit {

  @Autowired val app: WebApplication = null
  @Autowired val siteDAO: SiteDAO = null

  feature("Admin Login") {
    scenario("Correct username and password") {...}

I need to populate my ScalaTest tests with @Autowired fields from a Spring context, but most Scalatest tests (eg FeatureSpecs can't be run by the SpringJUnit4ClassRunner.class -

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="myPackage.UnitTestSpringConfiguration", loader=AnnotationConfigContextLoader.class)
public class AdminLoginTest {
    @Autowired private WebApplication app;
    @Autowired private SiteDAO siteDAO;

(Java, but you get the gist).

How do I populate @Autowired fields from an ApplicationContext for ScalaTest?

class AdminLoginFeatureTest extends FeatureSpec with GivenWhenThen with ShouldMatchersForJUnit {

  @Autowired val app: WebApplication = null
  @Autowired val siteDAO: SiteDAO = null

  feature("Admin Login") {
    scenario("Correct username and password") {...}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吻风 2024-11-21 12:24:52

使用 TestContextManager,因为它会缓存上下文,这样就不会在每次测试时都重新构建它们。它是从类注释配置的。

@ContextConfiguration(
  locations = Array("myPackage.UnitTestSpringConfiguration"), 
  loader = classOf[AnnotationConfigContextLoader])
class AdminLoginFeatureTest extends FeatureSpec with GivenWhenThen with ShouldMatchers {

  @Autowired val app: WebApplication = null
  @Autowired val siteDAO: SiteDAO = null
  new TestContextManager(this.getClass()).prepareTestInstance(this)

  feature("Admin Login") {
    scenario("Correct username and password") {...}
  }
}

Use the TestContextManager, as this caches the contexts so that they aren't rebuilt every test. It is configured from the class annotations.

@ContextConfiguration(
  locations = Array("myPackage.UnitTestSpringConfiguration"), 
  loader = classOf[AnnotationConfigContextLoader])
class AdminLoginFeatureTest extends FeatureSpec with GivenWhenThen with ShouldMatchers {

  @Autowired val app: WebApplication = null
  @Autowired val siteDAO: SiteDAO = null
  new TestContextManager(this.getClass()).prepareTestInstance(this)

  feature("Admin Login") {
    scenario("Correct username and password") {...}
  }
}
疯到世界奔溃 2024-11-21 12:24:52

这是一个使用 TestContextManager 作为可堆栈特征实现的版本(以便您可以拥有自己的 beforeAll()afterAll() 方法,如果需要的话) code> 来完成上下文生命周期。

我尝试了其他帖子中建议的原始 TestContextManager.prepareTestInstance() 解决方案,但注意到我的上下文没有关闭,这会导致使用 sbt 控制台时每次连续测试运行后产生副作用并积累垃圾。

@ContextConfiguration(classes = Array(classOf[SomeConfiguration]))
class SomeTestSpec extends FlatSpec with TestContextManagement {

  // Use standard Autowired Spring annotation to inject necessary dependencies
  // Note that Spring will inject val (read-only) fields
  @Autowired
  val someDependency: SomeClass = null

  "Some test" should "verify something" in {
    // Test implementation that uses injected dependency
  }

}

TestContextManagement Gist

import org.scalatest.{BeforeAndAfterAll, Suite}
import org.springframework.core.annotation.{AnnotatedElementUtils, AnnotationAttributes}
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.{TestContext, TestContextManager}
import org.springframework.test.context.support.DirtiesContextTestExecutionListener
import org.springframework.util.Assert

/**
 * Manages Spring test contexts via a TestContextManager.
 *
 * Implemented as a stackable trait that uses beforeAll() and afterAll() hooks to invoke initialization
 * and destruction logic, respectively.
 * Test contexts are marked dirty, and hence cleaned up, after all test methods have executed.
 * There is currently no support for indicating that a test method dirties a context.
 *
 * @see org.springframework.test.context.TestContextManager
 */
trait TestContextManagement extends BeforeAndAfterAll { this: Suite =>

  private val testContextManager: TestContextManager = new TestContextManager(this.getClass)

  abstract override def beforeAll(): Unit = {
    super.beforeAll
    testContextManager.registerTestExecutionListeners(AlwaysDirtiesContextTestExecutionListener)
    testContextManager.beforeTestClass
    testContextManager.prepareTestInstance(this)
  }

  abstract override def afterAll(): Unit = {
    testContextManager.afterTestClass
    super.afterAll
  }
}

/**
 * Test execution listener that always dirties the context to ensure that contexts get cleaned after test execution.
 *
 * Note that this class dirties the context after all test methods have run.
 */
protected object AlwaysDirtiesContextTestExecutionListener extends DirtiesContextTestExecutionListener {

  @throws(classOf[Exception])
  override def afterTestClass(testContext: TestContext) {
    val testClass: Class[_] = testContext.getTestClass
    Assert.notNull(testClass, "The test class of the supplied TestContext must not be null")

    val annotationType: String = classOf[DirtiesContext].getName
    val annAttrs: AnnotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType)
    val hierarchyMode: DirtiesContext.HierarchyMode = if ((annAttrs == null)) null else annAttrs.getEnum[DirtiesContext.HierarchyMode]("hierarchyMode")
    dirtyContext(testContext, hierarchyMode)
  }
}

Here's a version implemented as a stackable trait (so that you can have your own beforeAll() and afterAll() methods, if necessary) that uses TestContextManager to complete the context lifecycle.

I tried the raw TestContextManager.prepareTestInstance() solution suggested in other posts, but noticed that my contexts weren't getting closed which results in side effects and accumulating garbage after each successive test run when using the sbt console.

@ContextConfiguration(classes = Array(classOf[SomeConfiguration]))
class SomeTestSpec extends FlatSpec with TestContextManagement {

  // Use standard Autowired Spring annotation to inject necessary dependencies
  // Note that Spring will inject val (read-only) fields
  @Autowired
  val someDependency: SomeClass = null

  "Some test" should "verify something" in {
    // Test implementation that uses injected dependency
  }

}

TestContextManagement Gist

import org.scalatest.{BeforeAndAfterAll, Suite}
import org.springframework.core.annotation.{AnnotatedElementUtils, AnnotationAttributes}
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.{TestContext, TestContextManager}
import org.springframework.test.context.support.DirtiesContextTestExecutionListener
import org.springframework.util.Assert

/**
 * Manages Spring test contexts via a TestContextManager.
 *
 * Implemented as a stackable trait that uses beforeAll() and afterAll() hooks to invoke initialization
 * and destruction logic, respectively.
 * Test contexts are marked dirty, and hence cleaned up, after all test methods have executed.
 * There is currently no support for indicating that a test method dirties a context.
 *
 * @see org.springframework.test.context.TestContextManager
 */
trait TestContextManagement extends BeforeAndAfterAll { this: Suite =>

  private val testContextManager: TestContextManager = new TestContextManager(this.getClass)

  abstract override def beforeAll(): Unit = {
    super.beforeAll
    testContextManager.registerTestExecutionListeners(AlwaysDirtiesContextTestExecutionListener)
    testContextManager.beforeTestClass
    testContextManager.prepareTestInstance(this)
  }

  abstract override def afterAll(): Unit = {
    testContextManager.afterTestClass
    super.afterAll
  }
}

/**
 * Test execution listener that always dirties the context to ensure that contexts get cleaned after test execution.
 *
 * Note that this class dirties the context after all test methods have run.
 */
protected object AlwaysDirtiesContextTestExecutionListener extends DirtiesContextTestExecutionListener {

  @throws(classOf[Exception])
  override def afterTestClass(testContext: TestContext) {
    val testClass: Class[_] = testContext.getTestClass
    Assert.notNull(testClass, "The test class of the supplied TestContext must not be null")

    val annotationType: String = classOf[DirtiesContext].getName
    val annAttrs: AnnotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(testClass, annotationType)
    val hierarchyMode: DirtiesContext.HierarchyMode = if ((annAttrs == null)) null else annAttrs.getEnum[DirtiesContext.HierarchyMode]("hierarchyMode")
    dirtyContext(testContext, hierarchyMode)
  }
}
乖乖哒 2024-11-21 12:24:52

我尝试在 Spring 4 + Scala 2.11 中使用 Duncan 的答案,但出现以下错误:

java.lang.IllegalStateException: Test class [TestGateway] has been configured with @ContextConfiguration's 'locations' (or 'value') attribute {GatewayContextConfiguration}, but AnnotationConfigContextLoader does not support resource locations.

在配置 ContextConfiguration 而不是字符串时,我必须调整他的代码以使用类:

@ContextConfiguration( classes = Array(classOf[GatewayContextConfiguration]), 
                       loader = classOf[AnnotationConfigContextLoader])
class TestGateway extends FlatSpec with Matchers {

  @Autowired val gpClient: IGlobalPropsWSClient = null

  new TestContextManager(this.getClass()).prepareTestInstance(this)

  "Echo" should "return what it was sent." in {
    val gateway = new CasaWsGateway
    gateway.echo("This is a test") should be ( "This is a test" )
  }
}

I tried using Duncan's answer with Spring 4 + Scala 2.11 and I got the following error:

java.lang.IllegalStateException: Test class [TestGateway] has been configured with @ContextConfiguration's 'locations' (or 'value') attribute {GatewayContextConfiguration}, but AnnotationConfigContextLoader does not support resource locations.

I had to tweak his code to use a class when configuring the ContextConfiguration instead of a string:

@ContextConfiguration( classes = Array(classOf[GatewayContextConfiguration]), 
                       loader = classOf[AnnotationConfigContextLoader])
class TestGateway extends FlatSpec with Matchers {

  @Autowired val gpClient: IGlobalPropsWSClient = null

  new TestContextManager(this.getClass()).prepareTestInstance(this)

  "Echo" should "return what it was sent." in {
    val gateway = new CasaWsGateway
    gateway.echo("This is a test") should be ( "This is a test" )
  }
}
放肆 2024-11-21 12:24:52

如果您使用 Spring boot,您可以使用 TestContextManager (如其他注释建议的那样)和 @SpringBootTest 注释。

这是我使用 scalaTest 和 spring boot 测试控制器的方法:

@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = RANDOM_PORT)
class CustomerControllerIT extends FeatureSpec with GivenWhenThen with Matchers {

  @Autowired
  var testRestTemplate: TestRestTemplate = _
  new TestContextManager(this.getClass).prepareTestInstance(this)

  @LocalServerPort
  val randomServerPort: Integer = null

  val baseUrl = s"http://localhost:$randomServerPort"

  feature("Customer controller") {

    scenario("Find customer by id") {

      Given("a customer id")
      val id = 1

      When("a request to /customers/{id} is sent")
      val url = s"$baseUrl/customers/$id"
      val response = testRestTemplate.getForEntity(url, classOf[Customer])

      Then("we get a response with the customer in the body")
      response.getBody.getId shouldBe 1
      response.getBody.getName shouldBe "Bob"

    }

  }

}

这里有一篇关于如何使用 spring boot 和 ScalaTest 进行集成测试和单元测试的文章:
ignaciosuay.com/testing-spring-boot-with-scalatest/

If you are using Spring boot you could use the TestContextManager (as other comments suggested) and the @SpringBootTest annotation.

This is how I test a controller with scalaTest and spring boot:

@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = RANDOM_PORT)
class CustomerControllerIT extends FeatureSpec with GivenWhenThen with Matchers {

  @Autowired
  var testRestTemplate: TestRestTemplate = _
  new TestContextManager(this.getClass).prepareTestInstance(this)

  @LocalServerPort
  val randomServerPort: Integer = null

  val baseUrl = s"http://localhost:$randomServerPort"

  feature("Customer controller") {

    scenario("Find customer by id") {

      Given("a customer id")
      val id = 1

      When("a request to /customers/{id} is sent")
      val url = s"$baseUrl/customers/$id"
      val response = testRestTemplate.getForEntity(url, classOf[Customer])

      Then("we get a response with the customer in the body")
      response.getBody.getId shouldBe 1
      response.getBody.getName shouldBe "Bob"

    }

  }

}

Here there is a post about how to do integration tests and unit tests with spring boot and ScalaTest:
ignaciosuay.com/testing-spring-boot-with-scalatest/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文