have a play controller method
def insertDepartment = Action(parse.json) { request =>
MyDataSourceProvider.db.withSession{ implicit session =>
val departmentRow = DepartmentRow(1, Option("Department1"))
departmentService.insert(departmentRow)
}
}
note MyDataSourceProvider.db is providing slick.driver.PostgresDriver.simple.Database and creating a withSession provides an implicit session to departmentService.insert
when I test departmentService session is provided by a text fixture as mentioned in this post. sessionWrapper is a simple function which creates a session, provides that session to a test block and rolls back data after test finishes.
sessionWrapper { implicit session =>
val departmentRow = DepartmentRow(1, Option("Department1"))
departmentService.insert(departmentRow)
}
This works nicely and as expected by not polluting database when service tests run. tests should not persist anything in the db but rollback after executing successfully.
now when testing play controller need a way to use sessionWrapper. to be able to roll back controller tests in a similar fashion to service tests.
note MyDataSourceProvider.db.withSession in controller insertDepartment.
wrapping controller test with sessionWrapper has no significance since controller def isn't accepting any implicit session but using one from MyDataSourceProvider.db.withSession
what's the best way to handle this? tried creating a trait controller, to be able to inject impl for a trait so mixin can be different for a test and real code but haven't found a way to "pass" session for test and not for production code. Any ideas?
Since Slick is blocking, you don't need Action.async. I wonder why that even compiles, because I don't see a future there, but I am not that familiar with Play.
There are several alternatives for what you can do:
My favorite: not use transaction rollback for testing, but use a test database, which you re-create for each test.
pull out
val departmentRow = DepartmentRow(1, Option("Department1"))
departmentService.insert(departmentRow)
into a method and only test that method, not the controller
use sessionWrapper in the controller and let it check for a configuration flag, that tells it if it is in test mode and should do a rollback, or if it is in production mode.
Related
I've been using akka-http for a while now, and so far I've mostly logged things using scala-logging by extending either StrictLogging or LazyLogging and then calling the:
log.info
log.debug
....
This is kinda ok, but its hard to understand which logs were generated for which request.
As solutions for this go, I've only seen:
adding an implicit logging context that gets passed around (this is kinda verbose and would force me to add this context to all method calls) + custom logger that adds the context info to the logging message.
using the MDC and a custom dispatcher; in order to implement this approach one would have to use the prepare() call which has just been deprecated.
using AspectJ
Are there any other solutions that are more straightforward and less verbose ? It would be ok to change the logging library btw..
Personally I would go with implicit context approach. I'd start with:
(path("api" / "test") & get) {
val context = generateContext
action(requestId)
}
Then I'd would make it implicit:
(path("api" / "test") & get) {
implicit val context = generateContext
action
}
Then I would make the context generation a directive, like e.g.:
val withContext: Directive1[MyContext] = Directive[Tuple1[MyContext]] {
inner => ctx => inner(Tuple1(generateContext))(ctx)
}
withContext { implicit context =>
(path("api" / "test") & get) {
action
}
}
Of course, you would have to take context as an implicit parameter to every action. But, it would have some advantages over MDC and AspectJ - it would be easier to test things, as you just need to pass value. Besides, who said you only ever need to pass request id and use it for logging? The context could as well pass data about logged in user, its entitlements and other things that you could resolve once, use even before calling action and reuse inside action.
As you probably guessed, this would not work if you want the ability to e.g. remove logging completely. In such case AspectJ would make more sense.
I would have most doubts with MDC. If I understand correctly it has build in assumption that all logic would happen in the same thread. If you are using Futures or Tasks, could you actually guarantee such thing? I would expect that at best all logging calls would happen in the same thread pool, but not necessarily the same thread.
Bottom line is, all possible posiltions would be some variant of what you already figured out, so the question is your exact use case.
We are currently using the Play Framework and we are using the standard logging mechanism. We have implemented a implicit context to support passing username and session id to all service methods. We want to implement logging so that it is session based. This requires implementing our own logger. This works for our own logs but how do we do the same for basic exception handling and logs as a result. Maybe there is a better way to capture this then with implicits or how can we override the exception handling logging. Essentially, we want to get as many log messages to be associated to the session.
It depends if you are doing reactive style development or standard synchronous development:
If standard synchronous development (i.e. no futures, 1 thread per request) - then I'd recommend you just use MDC, which adds values onto Threadlocal for logging. You can then customise the output in logback / log4j. When you get the username / session (possibly in a Filter or in your controller), you can then set the values there and then and you do not need to pass them around with implicits.
If you are doing reactive development you have a couple options:
You can still use MDC, except you'd have to use a custom Execution Context that effectively copies the MDC values to the thread, since each request could in theory be handled by multiple threads. (as described here: http://code.hootsuite.com/logging-contextual-info-in-an-asynchronous-scala-application/)
The alternative is the solution which I tend to use (and close to what you have now): You could make a class which represents MyAppRequest. Set the username, session info, and anything else, on that. You can continue to pass it around as an implicit. However, instead of using Action.async, you make your own MyAction class which an be used like below
myAction.async { implicit myRequest => //some code }
Inside the myAction, you'd have to catch all Exceptions and deal with future failures, and do the error handling manually instead of relying on the ErrorHandler. I often inject myAction into my Controllers and put common filter functionality in it.
The down side of this is, it is just a manual method. Also I've made MyAppRequest hold a Map of loggable values which can be set anywhere, which means it had to be a mutable map. Also, sometimes you need to make more than one myAction.async. The pro is, it is quite explicit and in your control without too much ExecutionContext/ThreadLocal magic.
Here is some very rough sample code as a starter, for the manual solution:
def logErrorAndRethrow(myrequest:MyRequest, x:Throwable): Nothing = {
//log your error here in the format you like
throw x //you can do this or handle errors how you like
}
class MyRequest {
val attr : mutable.Map[String, String] = new mutable.HashMap[String, String]()
}
//make this a util to inject, or move it into a common parent controller
def myAsync(block: MyRequest => Future[Result] ): Action[AnyContent] = {
val myRequest = new MyRequest()
try {
Action.async(
block(myRequest).recover { case cause => logErrorAndRethrow(myRequest, cause) }
)
} catch {
case x:Throwable =>
logErrorAndRethrow(myRequest, x)
}
}
//the method your Route file refers to
def getStuff = myAsync { request:MyRequest =>
//execute your code here, passing around request as an implicit
Future.successful(Results.Ok)
}
I have a simple controller test that goes through the router:
"returns all reservations" should {
running(FakeApplication()) {
val Some(result) = route(FakeRequest(GET, "/reservations?envId=560d89ec5393af5d00bcfdf1"))
mustBeValidResponse(result)
contentAsString(result) must contain("environmentId")
}
}
But as you can see, it requires an ID as part of the URL, meaning my test is dependent on data that is in the database, which is no good. How do I create a fixture or something that the test will run against so the test runs without actually relying on data in the database?
I assume that the code accessing the database is not directly in the controller, but in another class which is injected to the controller. You should use module system indroduced in Play version 2.4. Then what you need is to replace the real implementation which accesses DB by a mock. More info here:
https://www.playframework.com/documentation/2.4.x/ScalaTestingWithGuice#Overriding-bindings-in-a-functional-test
I have two different kind of slick.driver.PostgresDriver.simple.Database vals
e.g.
implicit val db : slick.driver.PostgresDriver.simple.Database = ProdDataSource.db
and
implicit val testdb : slick.driver.PostgresDriver.simple.Database = TestDataSource.db
only difference is the database they are pointing to. testdb points to a test db which all the tests use.
I have parameterized all APIs which accept (implicit db:Database)
e.g.
def save(emp: Employee)(implicit db : Database): Employee = db.withSession{implicit session => ...}
reason to have Database as an implicit param so testing becomes easier and tests can pass in test Database.
Now when above save def is called from a Play Controller, it'll have to have an implicit val db =... given play controllers are object what's a better way to make controllers parameterized (e.g. if it's a class we can pass in a class param) to be able to test controllers properly with appropriate Database?
Current controller looks as below, which isn't tastable given implicit val db is using prod db. for test need to inject implicit val testdb.
object MyController extends Controller {
implicit val db = ProdDataSource.db
I don't want to use cake pattern which really makes code hard to work and ugly. what's the best way to achieve this?
If the controller was a class you could make it an implicit argument, but I doubt that you can tell play to pass the database into the controller as an argument. So either use mixin composition to have different controller instances for test and production. Or use play config to swap out the one provided by Play for both scenarios.
I am learning Play! and I have followed the To do List tutorial. Now, I would like to use Squeryl in place of Anorm, so I tried to translate the tutorial, and actually it works.
Still, there is one little thing that irks me. Here is the relevant part of my model
def all: Iterable[Task] = from(tasks) {s => select(s)}
and the corresponding action in the controller to list all tasks
def tasks = Action {
inTransaction {
Ok(views.html.index(Task.all, taskForm))
}
}
The view contains, for instance
<h1>#tasks.size task(s)</h1>
What I do not like is that, unlike in the methods to update or delete tasks, I had to manage the transaction inside the controller action.
If I move inTransaction to the all method, I get an exception,
[RuntimeException: No session is bound to current thread, a session must be created via Session.create and bound to the thread via 'work' or 'bindToCurrentThread' Usually this error occurs when a statement is executed outside of a transaction/inTrasaction block]
because the view tries to obtain the size of tasks, but the transaction is already closed at that point.
Is there a way to use Squeryl transaction only in the model and not expose these details up to the controller level?
Well. It's because of lazy evaluations on Iterable that require session bound (size() method).
This might work if you turn Iterable into List or Vector (IndexedSeq) I suppose.
from(tasks)(s => select(s)).toIndexedSeq //or .toList