Способы улучшить этот код - PullRequest
5 голосов
/ 17 сентября 2010

Я пытаюсь написать тестовый код для моего Java-приложения, используя Scalatest. Я полагал, что поскольку 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. Пожалуйста, помогите мне сделать его более похожим на скалу и более функциональным.

Ответы [ 3 ]

9 голосов
/ 18 сентября 2010

Вы не можете исправить уродливый тест, переписав тест.Вы можете исправить это, только перепроектировав тестируемый API.

Технически, это - это возможность написать уродливые тесты для хороших API, если вы попытаетесь действительно тяжело, очень злые, очень, глупые, очень пьяные или очень уставшие.Но написание уродливого теста требует усилий , а программисты ленивы, поэтому очень маловероятно, что кто-то напишет уродливый тест по своему выбору.Почти невозможно написать ужасные тесты: вы что-то вставляете, вы получаете что-то, вы проверяете, получили ли вы то, что ожидали.Вот и все.Там действительно нет ничего, что можно было бы унизить.

Тест просто использует API так же, как его использует пользователь API.Это в основном пример того, как правильно использовать API, который также, почти как побочный эффект, проверяет, действительно ли API реализован .Вот почему уродливый тест является хорошим индикатором для плохого проектирования API, а также почему тест-драйв разработки API - это хорошая вещь, даже если вы не используете TDD.

В этом конкретном случае яЯ могу увидеть немало способов улучшить API, хотя эти предложения обязательно являются неполными, поверхностными и упрощенными (не говоря уже о, вероятно, неправильными), поскольку я ничего не знаю о вашем домене:

  • Better Names : setRoot звучит так, как будто он устанавливает рут.Но, если false не является корнем вашей иерархии, я предполагаю, что значение на самом деле является тем, является ли этот тег корневым.Таким образом, оно должно быть названо isRoot или makeRoot или setIsRoot или что-то в этом роде.
  • Better Defaults : продолжить с setRoot, предполагая, что мое предположение вернои это устанавливает, является ли тег корневым, тогда значение по умолчанию является неправильным.По самому определению понятия «корень» может быть только один корень.Таким образом, вы заставляете своих пользователей указывать setRoot(false) каждый раз , за исключением того одного времени, когда они фактически определяют корень.Не корневые теги должны быть по умолчанию, и вы должны быть вынуждены только setRoot(true) для этого одного тега, который на самом деле является корневым.
  • Лучшие значения по умолчанию, часть II : setRole(null).Шутки в сторону?Вы заставляете своих пользователей явно устанавливать роль на unset ?Почему бы просто не сделать unset по умолчанию?В конце концов, тест называется «... когда ни роль, ни корень не определены», так зачем их определять?
  • Свободный API / Pattern Builder : если вы действительно необходимо создавать недопустимые объекты (но см. следующий пункт), по крайней мере, использовать что-то вроде Fluent API или Pattern 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)
  }
}
3 голосов
/ 17 сентября 2010

Поскольку этот конкретный тест просто вызывает группу сеттеров для объекта, реализованного в Java, вы не можете сделать что-то много, чтобы сделать его более кратким, функциональным или нестандартным.Вы можете удалить некоторые повторения с помощью чего-то вроде

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

Я не уверен, стоит ли оно того в этом случае.

3 голосов
/ 17 сентября 2010

Приведенный ниже код создает новый анонимный класс, но doStartTag возвращает ожидаемый результат:

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