How to set vals to be used in tests? - scala

I've been noticing a lot of repetition to set up each test with Play/specs2. I'm aware that you can create a trait that extends mutable.Before and use that to set up tests but any values created there seem to be out of scope for my tests.
What I would like to be able to do is set val user = User.create("User Name") in a class or trait before each test runs so I can have access to user later on in the test. How can this be achieved?
// SpecificationWithFixtures.scala
package models
import org.specs2.execute.{AsResult, Result}
import org.specs2.mutable._
import play.api.test.Helpers._
import play.api.test._
abstract class SpecificationWithFixtures extends Specification {
abstract class WithFakeDB extends WithApplication(FakeApplication(additionalConfiguration = inMemoryDatabase())) {
override def around[T: AsResult](t: => T): Result = super.around {
t
}
}
}
// UserSpec.scala
package models
import org.junit.runner._
import org.specs2.runner._
#RunWith(classOf[JUnitRunner])
class UserSpec extends SpecificationWithFixtures {
"User.create" should {
"save user in the database" in new WithFakeDB {
val user = User.create("User Name")
// Some test for user
}
}
"User.findAll" should {
"return a list of all users" in new WithFakeDB {
val user = User.create("User Name")
// Another test for user
}
}
}

What you can do is something like:
def withUser[T](test: User => T): T = test(User create "Username")
// or even more configurable
def withUser[T](name: String)(test: User => T): T = test(User create name)
// Then writing expectations you can do
"User Roger" in withUser("Roger") {
roger => // trivial example
roger.name must_== "Roger"
}
// or even
"User" in withUser("John") {
_.name must_== "John"
}
This kind of loan pattern is useful writing specs2.
In previous example it's user per expectation (in), but it can be used for a group of expectations (should, >>), or for all.
"User" should withUser("xyz") {
"exp1" in { ??? }
}

Related

Specs2 and Scalacheck - mixing ForEach context with properties

I'm writing Specs2 tests that use a temporary file and properties from ScalaCheck. Without properties it works fine :
import better.files.File
import org.specs2.execute.{AsResult, Result}
import org.specs2.mutable.Specification
import org.specs2.specification.ForEach
trait TmpDirContext extends ForEach[File] {
def foreach[R: AsResult](testWithFile: File => R): Result = {
val tmpDirCtx = File.temporaryDirectory()
AsResult(tmpDirCtx.apply(testWithFile))
}
}
class OkTest extends Specification with TmpDirContext {
import better.files._
"Example" should {
"work" in { tmpDir: File =>
tmpDir.exists must beTrue
}
}
}
val test = new OkTest
specs2.run(test)
If I add properties, it doesn't compile :
import org.scalacheck.Prop
import org.specs2.ScalaCheck
class KoTest extends Specification with ScalaCheck with TmpDirContext {
"KoTest" should {
"work" in { tmpDir: File =>
"for" ! Prop.forAll { value: Int =>
tmpDir.exists must beTrue
}
}
}
Error:(26, 16) could not find implicit value for evidence parameter of type org.specs2.execute.AsResult[better.files.File => org.specs2.specification.core.Fragment]
"work" in { tmpDir: File =>
I've managed to make it compile, but then the test fails seemingly because the ForEach from TmpDirContext has already disposed of a temporary folder:
class KoTest2 extends Specification with ScalaCheck with TmpDirContext {
"KoTest2" should {
"work" >> { tmpDir: File =>
Prop.forAll { value: Int =>
tmpDir.exists must beTrue
}
}
}
}
I guess I'm missing something... How to make it work and have the tmpDir available in the property testing?
You can try the following approach
import org.specs2.mutable.Specification
import java.io.File
import org.specs2.ScalaCheck
import org.scalacheck._
class KoTest extends Specification with ScalaCheck with TempDir { sequential
"KoTest" should {
"work" >> {
"for" >> prop { (tmpDir: File, value: Int) =>
tmpDir.exists must beTrue
}.after(deleteTmpDir)
}
}
}
trait TempDir {
implicit def arbitraryTempDir: Arbitrary[File] =
Arbitrary(tmpDir)
val tmpDir = new File("temp")
def deleteTmpDir = tmpDir.delete
}
It is presented here

How to test a custom directive / extract value from akka.http.scaladsl.server.Directive?

I have a custom directive with a function like the following that returns a Directive1[ValidatedParameters], where ValidatedParameters is just a simple case class:
class MyCustomDirective {
def validateParameters(...): Directive1[ValidatedParameters] = {
...
provide(ValidatedParameters(...))
}
}
I'm using it like this in my route:
myCustomDirective.validateParameters(top, skip, modifiedDate) {
(validatedParameters: ValidatedParameters) => {
However, I have a unit test where I'd basically like to call the above function and verify that ValidatedParameters is what I expect:
val actualResult: Directive1[ValidatedParameters] = new MyCustomDirective().validateParameters(...)
So actualResult is a Directive1[ValidatedParameters], is there a way I can get access to the ValidatedParameters case class within this directive from a unit test?
Here is a solution for writing a test case for the custom directive as mentioned here:
REQUEST ~> ROUTE ~> check {
ASSERTIONS
}
For a custom directive to get page and per_page query paramters:
case class Paginate(page: Option[String], perPage: Option[String])
trait PaginationDirective extends ParameterDirectives with RespondWithDirectives {
def withPagination: Directive1[Paginate] = {
parameters(
(
"page".as[String].?,
"per_page".as[String].?
)
).tmap { case (pageOpt, perPageOpt) => Paginate(pageOpt, perPageOpt) }
}
def logCurrentPage(currentPage: Int): Directive0 = {
mapResponse(response => {
logger.info(s"currentPage is $currentPage")
response
})
}
}
Test case for the above custom directive:
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalatest.{Matchers, WordSpec}
class PaginationDirectiveTest extends WordSpec with Matchers with PaginationDirective with ScalatestRouteTest {
"paginate directive" should {
"return page and per page parameters" in {
Get("/?page=1&per_page=25") ~> withPagination(i => complete(i.toString)) ~> check {
responseAs[String] shouldEqual "[Paginate(Some(1), Some(25))]"
}
}
"logCurrentPage" should {
Get("/hello") ~> logCurrentPage(5)(complete("Dummy msg")) ~> check {
//validate test here.
}
}
}
I ended up with the following:
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.testkit.ScalatestRouteTest
import de.heikoseeberger.akkahttpcirce.ErrorAccumulatingCirceSupport._
import org.scalamock.scalatest.MockFactory
import org.scalatest.{BeforeAndAfter, Matchers}
class MyTest extends ScalatestRouteTest {
...
"validate(top=None, skip=None, modifiedDate=None)" should "pass validation" in {
myCustomDirective.validate(top, skip, modifiedDate) {
(validatedParameters: ValidatedParameters) => {
validatedParameters shouldEqual expectedResult
complete("") // Needed to pass compilation
}
}
}
}

Reuse mock declaration across tests

I'd like to reuse mock declarations accross tests (if possible).
Here is a minimal non-working example using ScalaTest and Mockito. I'm expecting the ​yes​ value in the first test but I get the ​other​ value.
It seems that the latest Mockito.when is the one applied for all test clauses.
Is there a way to avoid declaring mocks in each in clause?
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.scalatest.{Matchers, WordSpec}
​
class ReuseMocksSpec extends WordSpec with Matchers with MockitoSugar {
"A test" when {
val service = mock[Service]
"sharing mocks among tests" should {
when(service.getVal).thenReturn("yes")
"get yes value" in {
service.getVal should be("yes")
}
}
"sharing mocks among other tests" should {
when(service.getVal).thenReturn("other")
"get other value" in {
service.getVal should be("other")
}
}
}
​
trait Service {
def getVal: String
}
}
I reviewed the way I designed it and am now using a function to build my mocks:
def withValue(value: String)(body: (Service => String)) = {
val service = mock[Service]
when(service.getVal).thenReturn(value)
body(service)
}
The test class would become:
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.scalatest.{Matchers, WordSpec}
class ReuseMocksSpec extends WordSpec with Matchers with MockitoSugar {
"A test" when {
"sharing mocks among tests" should {
"get yes value" in {
val value = withValue("yes") { service =>
service.getVal
}
value should be("yes")
}
}
"sharing mocks among other tests" should {
"get other value" in {
val value = withValue("other") { service =>
service.getVal
}
value should be("other")
}
}
}
def withValue(value: String)(body: (Service => String)) = {
val service = mock[Service]
when(service.getVal).thenReturn(value)
body(service)
}
trait Service {
def getVal: String
}
}
I don't know if it's the cleanest and easiest way to do it but it works...

How to implement Secure.Authenticator in scala Controller?

I am following this guide -
https://www.playframework.com/documentation/2.1.1/JavaGuide4#Implementing-authenticators
I have implemented the authenticator as below in Scala:
package controllers
import play.mvc.Security
import play.mvc.Http.Context
import play.mvc.Result
import play.mvc.Results
class Secured extends Security.Authenticator {
override def getUsername(ctx: Context) : String = {
return ctx.session().get("username");
}
override def onUnauthorized(ctx: Context) : Result = {
Results.redirect(routes.Application.login())
}
}
I applied it as an annotation to the hello action in the below controller.
package controllers
import services.UserServiceImpl
import models.User._
import play.api.mvc._
import play.mvc.Security.Authenticated
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
#Authenticated(classOf[Secured])
def hello = Action {
Ok("Hello")
}
def login = Action {
Ok("Login")
}
}
I am using Play 2.4 and play.api.mvc.Controller instead of play.mvc.Controller as in the guide. Is this the reason this is not working? How do I get it to work with play.api.mvc.Controller?
I figured an example would probably help explain things. If you want similar functionality in Play Scala you would do something like the following:
object AuthenticatedAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
request.session.get("username") match {
case Some(user) => block(request)
case None => Redirect(routes.Application.login())
}
}
You would then use this in your controller:
def hello = AuthenticatedAction { implicit request =>
Ok("Hello")
}

How to write a global object for table creation using Slick MultiDBCake principle?

I'm using Play framework btw. Slick does not help you "create" tables. Users have to create it by themselves, using tableQuery.ddl.create. When it was still Slick 1.0, I read a post about creating a Global.scalaand inside it looks like this:
import play.api._
import scala.slick.jdbc.meta.MTable
import play.api.Play.current
import play.api.db.slick._
import models.current.dao._
import models.current.dao.profile.simple._
object Global extends GlobalSettings {
override def onStart(app: Application) {
DB.withSession {implicit rs =>
databaseCreate(Articles, Labels, Article_Labels, Users, Messages)
def databaseCreate(tables: TableQuery[_]*) = {
for (table <- tables)
if (MTable.getTables(table.toString).list.isEmpty) table.ddl.create
}
}
}
}
However, since it is Slick 2.0 and I'm using the Cake pattern, this doesn't work anymore. I used to be able to pull Table class out and there is an attribute called name which I can call, but now with the new and improved Cake pattern, all I'm left with is TableQuery, which literally has nothing. (Cake pattern example is here: https://github.com/slick/slick-examples/blob/master/src/main/scala/com/typesafe/slick/examples/lifted/MultiDBCakeExample.scala)
My DAO class looks like this:
class DAO(override val profile: JdbcProfile) extends ArticleComponent with Article_LabelComponent with LabelComponent with MessageComponent with UserComponent with Profile {
val Articles = TableQuery[Articles]
val Article_Labels = TableQuery[Article_Labels]
val Labels = TableQuery[Labels]
val Messages = TableQuery[Messages]
val Users = TableQuery[Users]
}
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
How should I modify my Global.scala to create a table if a table is not present in the Database?
take a look : http://blog.knoldus.com/2014/01/20/scala-slick-2-0-for-multi-database/
and you can put directly ddl create statement in Glabal.scala:
object Global extends GlobalSettings
override def onStart(app: Application): Unit = {
Logger.info("Apllication has started")
try {
Connection.databaseObject.withSession { implicit session: Session =>
(Articles.ddl ++ Article_Labels.ddl ++ Labels.ddl ++ Messages.ddl ++ Users.ddl).create
Logger.info("All tables have been created")
} catch {
case ex: Exception => Logger.info(ex.getMessage() + ex.printStackTrace())
}
}
}
define connection object:
object Connection {
def databaseObject(): Database = {
Database.forDataSource(DB.getDataSource())
}
}