如何改进此代码

时间:2010-09-17 11:47:14

标签: unit-testing scala scalatest

我正在尝试使用Scalatest为我的java应用程序编写一些测试代码。我想,由于Scala具有更可读的语法,因此可以获得更易读的测试代码。

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

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 securityServiceMock = mock[SecurityService]

    val tag = new CheckRoleTag()
    tag.setSecurityService(securityServiceMock)

    tag.setGroup("group")
    tag.setPortal("portal")

    tag.setRoot(false)
    tag.setRole(null)

    tag.doStartTag should be(Tag.SKIP_BODY)
  }

}

我对这段代码感到非常失望。实际上,我需要用Java编写它。请帮助我使它更像scala和功能。

3 个答案:

答案 0 :(得分:9)

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

嗯,从技术上讲, 可能会为优秀的API编写丑陋的测试,如果您尝试真的很难,非常邪恶,非常,愚蠢,非常醉或非常累。但写一个丑陋的测试需要努力并且程序员很懒惰,所以有人不太可能通过选择编写丑陋的测试。写丑陋的测试几乎是不可能的:你坚持下去,你得到一些东西,你检查你是否得到了你所期望的。而已。那里真的没有什么可以搞的。

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

在这种特殊情况下,我可以看到很多方法来改进API,虽然这些建议必然是不完整的,浅薄的和简单化的(更不用说可能是错误的),因为我对你的域名一无所知: / p>

  • 更好的名字setRoot听起来像是在设置根目录。但是,除非false是层次结构的根,否则我认为实际设置的内容是此标记是否为根。因此,它应该命名为isRootmakeRootsetIsRoot或类似名称。
  • 更好的默认值:继续setRoot,假设我的猜测是正确的并且设置标记是否是根,那么默认是错误的方法。根据“root”概念的定义,只能有一个根。因此,您强制用户每次都指定setRoot(false) ,但实际定义根目录时一个时间除外。非根标记应该是默认标记,只应强制setRoot(true)一个标记实际 根目录。
  • 更好的默认值,第二部分setRole(null)。真的吗?您是强制用户明确设置角色取消设置?为什么不简单地取消默认设置?毕竟,测试被称为“...既没有角色也没有定义根”,为什么要定义它们呢?
  • Fluent API / Builder Pattern :如果确实必须构建无效对象(但请参阅下一点),至少要使用类似Fluent API或Builder模式的内容
  • 仅构造有效对象:但实际上,对象在构造时应始终有效,完整且完全配置。您不必构造对象,然后 配置它。

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

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)
  }
}

答案 1 :(得分:3)

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

...
(new CheckRoleTag{
   setSecurityService(mock[SecurityService])
   setGroup("group")
   setPortal("portal")
   setRoot(false)
   setRole(null)
} doStartTag) should be(Tag.SKIP_BODY)
...

答案 2 :(得分: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)
}

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