【问题标题】:Ways to improve this code改进此代码的方法
【发布时间】:2011-04-13 16:43:04
【问题描述】:

我正在尝试使用 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 和功能。

【问题讨论】:

  • 究竟是什么让您失望了?我认为这是tag.set... 的事情,所以你必须重构CheckRoleTag,也许还有SecurityService
  • @michael.kebe 问题是我不想更改我的 Java 代码以便从 scala 测试它。我希望 Java 看起来仍然像 Java。
  • CheckRoleTag 的构建器或真正的构造器将使您的 Java 和 Scala 变得更好!
  • 这是一个 JSP 标签。它需要setter和getter,所有这些字段都是从JSP文件中设置的。
  • 这是一个不幸的选择,试图使测试更性感:有些测试本质上是乏味和冗长的,不管是什么语言。

标签: unit-testing scala scalatest


【解决方案1】:

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

嗯,从技术上讲,如果你真的努力尝试,可能为好的 API 编写丑陋的测试,非常邪恶,非常,愚蠢,非常醉或非常疲倦.但是编写一个丑陋的测试需要努力,而且程序员很懒惰,所以不太可能有人会选择编写一个丑陋的测试。编写丑陋的测试几乎是不可能的:你插入一些东西,你取出一些东西,你检查你是否得到了你期望的东西。而已。那里真的没有什么可丑化的。

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

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

  • 更好的名字setRoot 听起来像是在设置根目录。但是,除非false 是您的层次结构的根,否则我假设 实际上 设置是此标记是否是根。因此,它应该命名为 isRootmakeRootsetIsRoot 或类似名称。
  • 更好的默认值:继续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)
  }
}

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这确实不是一个好方法,因为它没有按照预期使用的方式测试类。也就是说,它会跳过 setter 中可能存在的任何逻辑。
    【解决方案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)
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2017-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-16
      相关资源
      最近更新 更多