Google Guice 轻装上阵

PlayFramework 已全面向 Google Guice 演进, 由于我们的项目是从 Play 2.0 开始搭建, 现虽已升级到了 2.4.6, 但对 Guice 的还没抱得那么的紧, 有点积重而慢步前挪. Google Guice 也是一个依赖注入(DI) 容器, 据说它比 Spring 快了很多很多, 是轻量级项目或框架首选, Google 出品俱是精品.

既然它是轻量级的 DI, 那么必须呈上一个最轻量级的入门, 官方的 Get Started 文档其实啰嗦了点, 对于 BillingService 依赖的 CreditCardProcess, TransactionLog, 和 Receipt 都未有交待, 算不上一个完整项目. 所以我这里尽量简化, 又不偏离一个最简单的项目模型. 那就是下面那个例子, 它实现了在不同时间发出不同的问候.

项目需要依赖 Guice 及它的子依赖, 这得看你是用什么来管理依赖的, 依照 http://mvnrepository.com/artifact/com.google.inject/guice/4.0 根据实际情况加上依赖配置, 前面这个链接默认为 sbt 的, 我还真偏爱了 sbt, 若是 Maven 就配置 Maven 的 pom.xml 吧. Guice 的引入带上以下几个依赖包

   +-com.google.inject:guice:4.0
     +-aopalliance:aopalliance:1.0
     +-com.google.guava:guava:16.0.1
     +-javax.inject:javax.inject:1

Guice 目前是 4.0. 阅读全文 >>

sbt 任务间的依赖

项目中的 build.sbt 中发现定义任务时有 task2 <<= task1 map {...} 这样任务依赖的写法, 这个  <<= 方法有点晦涩难懂, 读过 sbt in action 之后才知道这是 sbt 0.12 或之前的做法, sbt 0.13 之后不这么用了, 直接访问下 task1.value 就行, 因此前面可改写为 task2 := {task1.value; ...}, 这也使得定义任务依赖时与普通任务一致风格了. 新的写法得益于 Scala 2.10 的宏特性, 后面还会讲到.

对于依赖于多个任务的情况, 在 sbt 0.13 前面分别是这样的, 假定有三个 Task(task1, task2, 和 task3)

val task1 = taskKey[Int]("task 1")
val task2 = taskKey[Int]("task 2")
val task3 = taskKey[Int]("task 3")

如果 task3 依赖于 task1 和 task 2

sbt 0.13 前后版本中的写法分别是

像任务的 .value 属性其实是一个宏定义, 源码 阅读全文 >>

ScalaTest + Selenium 集成测试

在 Play 1 和 Play 2 中都内置了 Selenium 集成测试工具, 这里自己尝试自己单独测试用 ScalaTest + Selenium 来做简单的集成测试. Selenium 可以支持内置的无界面 Java 实现的浏览器, 也可以用外部浏览器, 如 Safari, Firefox, Chrome, IE, Opera 或移动设备的浏览器. 使用不同外部浏览的方式或用插件(Safari/Firefox 等) 或是像借助于 Chromium 来驾驭 Chrome 浏览器.

而我们这里要用的 ScalaTest 2.2.6 在包 org.scalatest.selenium 有以下几个特质 Chrome, Driver, Firefox, HtmlUnit, InternetExplorer, Page, Safari 和  WebBrowser. 由此可以看出 ScalaTest 默认支持的浏览器. 若单论 Selenium 本身, 它可强大的暂时超乎我的想像, 可以自建服务器, 选择浏览器分发测试任务. 我们知道使用 Selenium 的好处是不光可以像通常那样断言页面静态文本, 还能执行 Javascript 脚本, 所以可断言动态内容.

本人开发环境为 Mac OS, 可以成功让 Selenium 测试跑在内置 Java 浏览器, 和外置的  Safari, Chrome 浏览器中, Firefox 的插件未安装成功.

让事实说话, 仍然让一个最简单的例子自己说话, 创建的是一个 sbt 项目, 尽量把目录最简化, 只有 build.sbt 文件和 test/IntegrationTest.scala 测试代码, 内容分别为 阅读全文 >>

建立 Play 2 框架一样的目录布局

sbt 项目继承并扩展了 Maven 的默认项目布局, 加入了 Scala 代码的支持, 所以目录如 Shell 命令 mkdir -p src/{main,test}/{java,scala,resources} 生成的目录结构, 即

.
└── src
    ├── main
    │   ├── java
    │   ├── resources
    │   └── scala
    └── test
        ├── java
        ├── resources
        └── scala

这个目录目录虽然很清晰, 但把 Java 和 Scala 代码拆在两处没多大必要, 其次是层次多了点. 因使用 Play Framework 时日有点久了, 比较习惯于 Play 2 改造后的项目布局. 我们启动到 Play 2 的 activator(其实就是加入了定制的 sbt) 控制台, 用命令看它的目录布局 阅读全文 >>

sbt 中单元测试并发执行

此次研究的目的原本是要使得 Play Framwork 2 中单元测试能够并发执行, 包括 JUnit 和 Spec 的测试用例, Play 2 的 activator 就是一个 sbt 的包装. 开发中发现我们 Play 2 中的单元测试是按序执行的, 实际上 sbt 下测试用例默认是并发执行的. 之所以 Play 2 的单元测试是按序的, 是因为 activator 设置了把 sbt 的两个属性 fork in Test := trueparallelExecution in Test := false, 见 PlaySettings.scala, 它们默认分别为 false 和 true. 这使得默认设置下 Play 2 中的所有测试无法并发执行.

sbt 默认的 fork 是 false, Play 2 改为 true 之后便可以使用 javaOptions in Test := "-Dkey1=value1" (注: 如果 fork 为 false 的话, javaOptions 将无效.) 往单元测试中参数了, 这也是为什么在 Play 2 的单元测试中无法获得启动 sbt 时(像 sbt -Dkey1=value) 的参数, 不同一个 JVM 啊.

那是不把 Play 2 的 fork in TestparallelExecution in Test 分别改回成 false 和 true 就可以让测试用例并发执行了呢? 答案是 Yes. 但我们得相信 Play 2 把它们预设为 true 和 false 是有它的用意的, 比如集成测试的每个用例都会开启本地的 3333 端口, 如果让两个集成测试同时执行将会造成端口冲突. 细致说来, Play 2 其实是懒政, 只管一刀切而让所有测试按序执行而影响了效率, 如能利用好 sbt 的测试分组机制是可以达到测试的并发执行的.

这里引出 sbt 执行测试的几个机制:

1) sbt 总是对测试进行分组, 默认时所有的测试都包含在 <default> 组中, 可用 show testGrouping 查看, 如

阅读全文 >>

查看 sbt 项目的依赖关系树

sbt 是借助于 ivy 来管理项目依赖, 像 Maven 项目中可以用 dependency:tree 来显示依赖树, 那么对于 sbt 项目该如何查看项目依赖关系呢? 本文提及了三种方式来显示项目依赖, 它们是 Shell 脚本, 自定义 sbt 任务, 和 sbt-dependency-plugin 方式. 最后一个办法使得我们也能用 dependencyTree 显示出 Maven 的  dependency:tree 效果来, 还有更酷的的.

> dependencyTree
[info] default:test_2.10:0.1-SNAPSHOT [S]
[info]   +-ch.qos.logback:logback-classic:1.0.13
[info]     +-ch.qos.logback:logback-core:1.0.13
[info]     +-org.slf4j:slf4j-api:1.7.5
[info]
[success] Total time: 0 s, completed Apr 5, 2016 12:29:53 AM

下面是探索的全部过程.

通过 sbt 控制台的 tab  自动完成或用 help .*[Dd]ependenc.* 命令再进一步过滤出与依赖比较接近 sbt 控制台任务 阅读全文 >>