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
Related
I am writing one Spark application using Scala and I am trying to write unit tests for a method which will load data from Hive table, do some processing on it and return the result as a data frame.
Method looks as shown below:
private def filterData(SqlContext context, tableName): DataFrame = {
val table = context.table(tableName)
val selectColumnList = Seq("colA", "colB")
table.select(selectColumnList).filter(table.col("colC") > 100)
}
I would like to know how can I mock SqlContext.table() method so that I can supply some test data whenever it is called or is there any other way to achieve it using Scala?
Don't mock what you don't own.
When you do that, you're assuming you know how that code will behave, and therefore you can provide the result of invoking that code in your test. This assumption is likely to blow up in your face, especially when you upgrade the library version - tests pass, production breaks.
Instead, write an Adapter for it, and then use a mocked instance of it when testing units that use it. The adapter separates your code from the outside world. To test the adapter itself, you'll have to write an Integration Test, that spins up spark (or whatever implementation of the adapter) and checks that the adapter works correctly.
So, your adapter could contain the function you described above, you'd need to write an Integration test that checks it against real Spark. When you use the adapter, though, you can mock it.
trait DataProcessor {
def filterData(SqlContext context, tableName): DataFrame
}
class SparkDataProcessor extends DataProcessor {
override def filterData(SqlContext context, tableName): DataFrame = {
...
}
}
And the test for the class that uses it:
class MyThingieTest extends Spec {
"should use the data processor" >> {
val mockDataProcessor = mock[DataProcessor]
mockDataProcessor.filterData(context, tableName) returns ...
MyThingie(mockDataProcessor).doSomething must beEqualTo(...)
}
}
This way you can specify what the adapter returns.
Note - make sure to not leak 3rd party implementation in the adapter API. It should only return your data structures.
Here is another great article that talks about this very subject.
I have an Play 2.5.3 application which uses Slick for reading an object from DB.
The service classes are built in the following way:
class SomeModelRepo #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) {
val dbConfig = dbConfigProvider.get[JdbcProfile]
import dbConfig.driver.api._
val db = dbConfig.db
...
Now I need some standalone Scala scripts to perform some operations in the background. I need to connect to the DB within them and I would like to reuse my existing service classes to read objects from DB.
To instantiate a SomeModelRepo class' object I need to pass some DatabaseConfigProvider as a parameter. I tried to run:
object SomeParser extends App {
object testDbProvider extends DatabaseConfigProvider {
def get[P <: BasicProfile]: DatabaseConfig[P] = {
DatabaseConfigProvider.get("default")(Play.current)
}
}
...
val someRepo = new SomeModelRepo(testDbProvider)
however I have an error: "There is no started application" in the line with "(Play.current)". Moreover the method current in object Play is deprecated and should be replaced with DI.
Is there any way to initialize my SomeModelRepo class' object within the standalone object SomeParser?
Best regards
When you start your Play application, the PlaySlick module handles the Slick configurations for you. With it you have two choices:
inject DatabaseConfigProvider and get the driver from there, or
do a global lookup via DatabaseConfigProvider.get[JdbcProfile](Play.current), which is not preferred.
Either way, you must have your Play app running! Since this is not the case with your standalone scripts you get the error: "There is no started application".
So, you will have to use Slick's default approach, by instantiating db directly from config:
val db = Database.forConfig("default")
You have lot's of examples at Lightbend's templates.
EDIT: Sorry, I didn't read the whole question. Do you really need to have it as another application? You can run your background operations when your app starts, like here. In this example, InitialData class is instantiated as eager singleton, so it's insert() method is run immediately when app starts.
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.
I have a simple Play application in which I need to check url being called and use different database accordingly. I know that it's easy to access current url in the controller, but for this to work I need to access it in the model.
Passing the url from controller to each call of a model method would be too big of an inconvenience. Is there any other way to solve this problem?
Play Framework 2.2.1 / Scala 2.10.3
UPDATE: This is my basic example
Controller (Application.scala):
package controllers
import play.api._
import play.api.mvc._
import models.Data
object Application extends Controller {
def index = Action {
//Call to model method - model should somehow get the URL without it being passed as a param
val smth: String = Data.getSmth()
Ok(smth);
}
}
Model (Data.scala):
package models
object Data {
def getSmth: Option[String] = DB.withSession {
val db = //this is where I need the url to decide which database to use
sql"""SELECT #$db.smth FROM smthTable""".as[String].firstOption
}
}
So, this is by design in the Play Scala API - there is no magic context, if you want data you will have to pass it along to whatever piece of your code that needs it.
You will have to take the url as a parameter of some kind, you could do it like this:
case class MyModel(someData: String, requestUrl: String)
object MyModel {
def apply(someData: String, request: Request) =
new MyModel(someData, request.url)
}
This would clearly express the dependency, but in your particular app you might call this from every request and want to avoid having to repeat providing that parameter, in that case you can use Scala implicits which makes the compiler look for a matching implicit instance that is of the same type in the current scope (you can read more about this here: http://www.scala-lang.org/old/node/114).
object MyModel {
def apply(someData: String)(implicit request: Request) =
new MyModel(someData, request.url)
}
which could then be called from a controller action like this
def myAction = Action { implicit request =>
val model = MyModel("blablabla")
...
}
Of course it may be a bad idea to tightly couple your model to the play Request API and you should probably introduce your own class to represent this 'context', you could then implicitly convert from Request to YourContext in you controllers and have the model implicitly use YourContext instead.
If all this sounds like gibberish to you, you should probably start with actually learning Scala before trying to build a web app in Scala. There are lots of good books nowadays ('Scala for the impatient' for example) as well as a multitude of good online resources (the neophytes guide to scala is a good one).
Good luck!
I'm writing a griffon application with JavaFX and the JPA plugin. I have a service I'd like to test - this service makes use of the JPA plugin (withJpa {...}) and it's this database access that I want to test.
So, I want to write this test so it inserts some data, then check that the service produces the right answer thus verifying the sql query is correct.
I've written a simple test:
class ReportServiceTests extends GriffonUnitTestCase {
GriffonApplication app
public void testStats() {
println app.getServices()
println app.getControllers()
}
}
but I cannot get hold of a valid service - both the println statements above produce "[:]".
How do I get hold of the 'ReportService' instance and exercise it against the database? I don't want to mock the database interaction.
Thanks.
There's no need to mock the database. As explained in http://griffon.codehaus.org/guide/latest/guide/testing.html#integrationTesting applications reach the INITIALIZE phase during integration testing. Addons get initialized during this phase. Services on the other hand get initialized lazily as they are pulled in by MVC members when instantiated: they do not get instantiated out-of-the-box if you call app.getServices(). However you can instruct the application to eagerly instantiate all services, this will make your code work as expected; just add the following flag to Config.groovy
griffon.services.eager.instantiation = true
More info on services can be found at http://griffon.codehaus.org/guide/latest/guide/controllersAndServices.html#services