5

我正在尝试使用 Scalatest 为我的 java 应用程序编写一些测试代码。我想,既然 Scala 有更多可读的语法,它会产生更可读的测试代码。

到目前为止,这是我管理的:

包 com.xyz

导入 org.scalatest.FlatSpec
导入 org.scalatest.matchers.ShouldMatchers
导入 com.xyz.SecurityService
导入 org.mockito.Mockito._
导入 org.scalatest.mock.MockitoSugar
导入 org.mockito.Matchers._
导入 javax.servlet.jsp.tagext.Tag

类 CheckRoleTagSpec 使用带有 MockitoSugar 的 ShouldMatchers 扩展 FlatSpec {

  “CheckRole 标签”的行为

  它应该“在既没有定义角色也没有定义根时允许访问”{
    val securityServiceMock = mock[SecurityService]

    val 标记 = 新 CheckRoleTag()
    tag.setSecurityService(securityServiceMock)

    tag.setGroup("组")
    tag.setPortal("传送门")

    tag.setRoot(假)
    tag.setRole(null)

    tag.doStartTag 应该是(Tag.SKIP_BODY)
  }

}

我对这段代码很失望。这实际上与我需要用 Java 编写的内容相同。请帮助我使它更像scala和功能。

4

3 回答 3

9

您无法通过重写测试来修复丑陋的测试。您只能通过重新设计正在测试的 API 来修复它。

好吧,从技术上讲,如果你真的很努力,很邪恶,很愚蠢,很醉或很累,就有可能为好的 API 编写丑陋的测试。但是编写丑陋的测试需要付出努力,而且程序员很懒惰,因此不太可能有人会选择编写丑陋的测试。编写丑陋的测试几乎是不可能的:你插入一些东西,你取出一些东西,你检查你是否得到了你期望的东西。就是这样。那里真的没有什么可丑化的。

测试使用 API 的方式与 API 用户使用它的方式相同。它基本上是一个如何正确使用 API 的示例,几乎作为副作用,碰巧检查了 API 是否正确实现。这就是为什么一个丑陋的测试是糟糕的 API 设计的一个很好的指标,这也是为什么测试驱动 API 设计是一件好事,即使你不做 TDD。

在这种特殊情况下,我可以看到很多改进 API 的方法,尽管这些建议必然是不完整的、肤浅的和简单化的(更不用说可能是错误的),因为我对您的领域一无所知:

  • 更好的名字setRoot听起来像是在设置根。但是,除非false是你的层次结构的根,否则我假设它实际设置的是这个标签是否是根。所以,它应该被命名为isRootor makeRootorsetIsRoot或类似的东西。
  • 更好的默认值:继续setRoot,假设我的猜测是正确的并且这设置了标签是否是根,那么默认值是错误的。根据“根”概念的定义,永远只能有一个根。所以,你强迫你的用户每次都指定setRoot(false) 一次,除了他们实际定义根的那一次。非根标签应该是默认设置,您应该只强制setRoot(true)使用那个实际上是根标签标签
  • 更好的默认值,第二部分setRole(null)。严重地?您是在强迫您的用户明确角色设置为未设置?为什么不简单地将 unset 设为默认值?毕竟,测试被称为“......当既没有定义角色也没有定义根”,那么为什么要定义它们呢?
  • Fluent API / Builder Pattern:如果你真的必须构造无效对象(但请参阅下一点),至少使用 Fluent API 或 Builder Pattern 之类的东西。
  • 仅构造有效对象:但实际上,对象在构造时应该始终是有效的、完整的和完全配置的。您不必构造一个对象,然后对其进行配置。

这样,测试基本上变成了:

package com.xyz

import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
import com.xyz.SecurityService
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.mockito.Matchers._
import javax.servlet.jsp.tagext.Tag

class CheckRoleTagSpec extends FlatSpec with ShouldMatchers with MockitoSugar {
  behavior of "CheckRole tag"
  it should "allow access when neither role nor root defined" in {
    val tag = new CheckRoleTag(mock[SecurityService], "group", "portal")

    tag.doStartTag should be(Tag.SKIP_BODY)
  }
}
于 2010-09-17T20:06:50.487 回答
3

下面的代码创建新的匿名类,但doStartTag按预期返回结果:

...
(new CheckRoleTag{
   setSecurityService(mock[SecurityService])
   setGroup("group")
   setPortal("portal")
   setRoot(false)
   setRole(null)
} doStartTag) should be(Tag.SKIP_BODY)
...
于 2010-09-17T12:56:02.980 回答
3

由于这个特定的测试只是在用 java 实现的对象上调用一堆 setter,因此您无法做很多事情来使其更简洁、更实用或更小。你可以用类似的东西删除一些重复

it should "allow access when neither role nor root defined" in {
  val securityServiceMock = mock[SecurityService]

  val tag = new CheckRoleTag()

  locally { 
    import tag._
    setSecurityService(securityServiceMock)
    setGroup("group")
    setPortal("portal")
    setRoot(false)
    setRole(null)
  }

  tag.doStartTag should be(Tag.SKIP_BODY)
}

我不确定在这种情况下是否真的值得。

于 2010-09-17T14:24:53.040 回答