首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何为我的无标记代数编写单元测试?

如何为我的无标记代数编写单元测试?
EN

Stack Overflow用户
提问于 2020-06-21 04:13:51
回答 1查看 81关注 0票数 1

我有一个代数解释器,我想为它写一个单元测试。

解释器如下:

代码语言:javascript
运行
复制
final case class LiveDbConnector[F[_] : MonadError[*[_], Throwable]](env: Environment[F]) extends DbConnector[F] {
  override def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams] =
    (for {
      a <- OptionT(env.get(EnvVariable(url.v)))
      b <- OptionT(env.get(EnvVariable(user.v)))
      c <- OptionT(env.get(EnvVariable(pw.v)))
    } yield DbParams(DbUrl(a.v), DbUser(b.v), DbPw(c.v)))
      .value
      .flatMap {
        case Some(v) => v.pure[F]
        case None => DbSettingError.raiseError[F, DbParams]
      }
}

代数如下:

代码语言:javascript
运行
复制
trait DbConnector[F[_]] {
  def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams]
}

具体实现如下:

代码语言:javascript
运行
复制
final case class DbParams(url: DbUrl, user: DbUser, pw: DbPw)

object DbConnector {

  def impl[F[_] : MonadError[*[_], Throwable]](env: Environment[F])
  : DbConnector[F] =
    new LiveDbConnector[F](env)

} 

我使用测试框架https://scalameta.org/munit/

如何为上面的代数编写单元测试?

EN

回答 1

Stack Overflow用户

发布于 2020-06-24 20:49:12

在几乎每个测试框架中,您都可以通过同步调用它来实现这一点

代码语言:javascript
运行
复制
// given
val env: Environment[IO] = ...
val connector: DbConnector[IO] = DbConnector.impl[OP](env)
val url: DbUrl = ...
val user: DbUser = ...
val pw: DbPw = ...

// when
val result = connector.read(url, user, pw).attempt.unsafeRunSync

// then
val expected: DbParams = ...
assert(result == Right(expected))

因为MUnit本身也支持Future,所以你也可以这样做:

代码语言:javascript
运行
复制
// given
val env: Environment[IO] = ...
val connector: DbConnector[IO] = DbConnector.impl[OP](env)
val url: DbUrl = ...
val user: DbUser = ...
val pw: DbPw = ...

// when
connector.read(url, user, pw).attempt.unsafeToFuture.map { result =>
  // then
  val expected: DbParams = ...
  assert(result == Right(expected))
}

有了F,你就可以灵活地选择最容易测试的实现:cats.effect.IOcats.effect.SyncIOmonix.eval.Task等等。不同的测试框架只是在你如何在套件中组织你的测试,你可以使用什么类型的匹配器,有时还有可用的集成,但是你可以看到,即使没有集成,你也能够编写测试。

如果代数的每个实现都有仅依赖于输入的输出,并且每个实现都遵循一些约定,那么您可以为其定义规则

代码语言:javascript
运行
复制
class DbConnectorLaws[F[_]: MonadError[*[_], Throwable](
  connector: DbConnector[F]
) {

  // explicitly expressed contracts that tested class should fulfill

  private def expectedReadOuput(dbUrl: DbUrl, user: DbUser, pw: DbPw) = ...

  def nameOfReadContract(dbUrl: DbUrl, user: DbUser, pw: DbPw): F[Unit] =
    connector.read(dbUrl, user, pw).map { result =>
       // Cats laws has some utilities for making it prettier
      assert(result == expectedReadOuput(dbUrl, user, pw))
    }
}

然后你可以用Scalacheck来测试它

代码语言:javascript
运行
复制
import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll

// actual test with laws (cats call them discipline)
trait DbConnectorTests {
  val laws: DbConnectorLaws[IO] // simplified, study cats laws if you need it
    
  def readContract()(
    implicit 
    dbUrls: Arbitrary[DbUrl]
    users: Arbitrary[DbUser]
    pws: Arbitrary[DbPw]
      // also other implicits if necessary
  ) = {
    implicit val input = for {
      url  <- dbUrls
      user <- users
      pw   <- pws
    } yield (url, user, pw)
  
    // simplified as well
    forall { case (url: DbUrl, user: DbUser, pw: DbPw) =>
      laws.nameOfReadContract(url, user, pw).unsafeRunSync // throws if assertion fail
    }
  }
}
代码语言:javascript
运行
复制
val test = new DbConnectorTests { val laws = new DbConnectorLaws[IO](implementation) }

test.readContract()

然而,你的接口似乎是依赖于实现的,而且是独立的,它不提供任何可以通过这种方式测试的契约。我提到它只是因为在其他问题中,你问了关于“法律”的问题。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62491153

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档