-
-
Notifications
You must be signed in to change notification settings - Fork 283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exception when unit testing an API call using Slick #345
Comments
@RichyHBM Hi, I can't immediately see what is the reason for the exception, also considering we have similar tests in the sample projects, e.g., in https://github.com/playframework/play-slick/blob/master/samples/computer-database/test/ApplicationSpec.scala. Unfortunately, I won't have time to look into it in the foreseeable future, so if you'd like to help out and do some detective work, please go for it :-) |
@dotta I have tried a number of things:
The reason I believe its an evolutions issue is when running just the API test without any other tests, I get a Table does not exist error Also after changing from an in memory database to a file one my database trace file looks like:
I also don't know if this is strictly a play-slick issue, but play framework doesn't seem to think its them, and neither do slick. |
@dotta I believe I have confirmed this, Running this as the test
Makes it pass, I can add as many API tests into it and they all pass. However if I add in a normal database test:
Any API tests that are run after it will fail! |
Nice findings! The evolutions integration in play-slick is very minimalist as you can see here. All it does is providing an implementation of the Play Database abstraction that uses Slick underneath. From your last message is almost seems to me that the Database is being closed after running a test that triggers the Slick evolutions support. Not sure why that is the case, but I think you are pretty close to find the reason for the issue. Thanks a ton for looking into this! |
Well it looks to be some issue with either the tear down or the setup when using a combination of things, Specs, H2, Slick.. I had a look but can't seem to find the issue, so instead I wrote a (very ugly) Scala class that will just run all the defined tests in a single wrapped Spec which avoids this issue. I.E. I can just do:
Which produces:
I don't know if this bug is actually a play-slick issue so feel free to close it if you think best |
I think it's worth keeping it open until we have full resolution. Thanks for providing a workaround, I'm sure people will find it useful. |
Just thought I would leave a little more information on the workaround that I have noticed this weekend, my original method using the class that runs tests within a single test didn't really work as expected once I moved it from my simple example to my larger project, it would fix some tests but others would still fail. Instead I have found a solution that seems to fix this, or at least fully gets around it.
I know the only tests that are going to run into this issue are ones within the controllers package, so I fork these tests and run them inside a new JVM which makes sure what ever issue that is causing this is no longer there, I keep all other tests running in the current JVM as creating a new one adds about half a second to each test suite. If you wanted to you could mark tests with this specific issue and not for for all tests under controller but this works for now. However, this does make SBT report erroneous quantities of tests: I think ideally you would just order the tests so that all the problematic ones are run first and then all other ones are run, but there doesn't seem to be a way to specify SBT test ordering its just the order in which the JVM returns the test Specifications |
Final thing just for anyone that may come across this having the same issue, this is how I finally fixed it. I have added this to my SBT, it sorts test names by their lexicographical value and then puts them all into the same Group. Having them in the same group is important as its what makes SBT report the correct number of tests!
I then created a package within my test folder named Finally, all requests need to be made within the same Specification example, so for example:
Annoyingly this means that you don't get a test per API call, but at least they all get tested (as long as none of them throw an error) The main work around for this whole issue is having your API test run before any others, and within the same FakeApplication scope. The method I have outlined feels a bit hacky but I have spent all weekend trying to figure out a way of doing this in a more clean fashion and I just cant think of any, so if anyone figures out a nicer way to do it please do say! |
I'm seeing this problem as well... a very simple test application, single table. Using specs2 for a basic test: first insert a row, then query for a single row by id. class TestCartPersistence extends PlaySpecification with ScalaCheck with AutomationTestUtilities with Generators with TestLogging {
"A CartRepository" should {
"store a cart in the database" in new WithApplication {
val c: CartModel = CartModel(None, None, None, None, CartStatus.Prepurchase, CartType.Journey, 1L, None)
val f: Future[CartModel] = CartRepository.save(c)
f must not beNull
}
"be able to retrieve a list of people in the database" in new WithApplication {
val f: Future[Option[CartModel]] = CartRepository.findById(1L)
f must not beNull
}
}
} The second test fails with:
|
A little more information on my previous post: Adding to @RichyHBM's findings – indeed, I can execute several Slick calls within the same test case so long as its in a single WithApplication: class TestCartPersistence extends PlaySpecification with ScalaCheck with AutomationTestUtilities with Generators with TestLogging {
"A CartRepository" should {
// This will run just fine...
"store a cart in the database a couple times" in new WithApplication {
val c: CartModel = CartModel(None, None, None, None, CartStatus.Prepurchase, CartType.Journey, 1L, None)
val f: Future[CartModel] = CartRepository.save(c)
f must not beNull
val c2: CartModel = CartModel(None, None, None, None, CartStatus.Prepurchase, CartType.Journey, 1L, None)
val f2: Future[CartModel] = CartRepository.save(c2)
f2 must not beNull
}
// But this will blow up :-(
"be able to retrieve a list of people in the database" in new WithApplication {
val f: Future[Option[CartModel]] = CartRepository.findById(1L)
f must not beNull
}
}
} |
@zbeckman Interesting that you have experienced this when interacting directly with your DAO (CartRepository) I only experience this when the DAO is called from a controller (that is called from the test) This does not cause me any exceptions:
Where fakeApp is:
|
Agreed. Last night I removed play-slick from my configuration, and am now using straight up Slick (3.1.1), and it's working fine. So there must be something in the play-slick integration that's causing this problem. FYI, I also disabled evolutions, but that did not seem to affect the problem in any way... |
Same issue here @RichyHBM workarounds dosn't seem to help but I might be missing something. I am not using the evolutions but I am using Databases.withInMemory and then extends WIthApplication and overriding the arround method to run my schema creation
seems to run ok but returns an error because it cant find the schema dbo
Is my specific error |
Hi Everyone, sorry to hear so many of you are hitting the same problem. Unfortunately, I don't have any spare cycle at the moment to look at it, but I'd encourage you to try to debug the issue. After all, play-slick is a really small module, so there isn't a lot of code that needs to be understood. Just set a breakpoint in your test and explore what's happening on the play-slick code, hopefully the issue will reveal itself quickly. |
@zbeckman @ir1sh I just noticed something, if I wrap the offending code inside a DAO interaction it works! For example, if this would fail
By wrapping it in the map of a DAO it works!
@dotta Not expecting you to jump on this right away! :) This does seem more and more like the evolutions aren't being applied correctly in some circumstances |
@ir1sh You are completely right! I had not realised this because I had been writing all my tests in the style of:
But this has actually not been running them properly! Inserting a print inside the map would not print anything out.. I have begun changing my tests to use Await and now they are all failing for this reason. What version of Play are you using? When I try to use Databases.withInMemory it complains Databases can not be found |
play 2.4.x I Actually dont know where to look to find the x value Here is my complete database helper for my tests. Commenting out the first line in the for comprehension in createTables function gives the behaviour I described earlier I am not using evolutions. I will try your dao trick later and see if that helps.
|
@ir1sh If you mean wrapping the tests inside a DAO result map then don't do this, I didn't realise the map returns instantly so the test never actually runs What I am now doing, and seems to work is creating a global Application for the unit tests:
And removing the WithApplication from all my specifications, that way they all use the unique application |
@RichyHBM ok cool I will try that, do you know if you can give each test class a fresh db with this global application method? I don't want my tests to rely on state left in the db from previously run tests. |
@ir1sh Something like this should hopefully work
Have all your Specifications extend FreshDatabaseSpecification |
@ir1sh Actually, this is probably better as I have had the above occasionally throw exceptions because the Application hasn't been started before the before is called.
And then just:
|
@RichyHBM Only getting back to this now. How are you creating the h2 db in the first place? Databases.withInMemory elsewhere? Also what is DBApi? |
H2 database is created by using a FakeApplication with some custom config
The DBApi is part of play and provides access to the databases |
Thanks @RichyHBM this approach seems to work, at least I get different errors now 👍 |
I have the same problem here! |
@RichyHBM Your code is worked? I get exceptions/fails from paralell like execution after I "totally" turned off paralell execution. Helper:
Build.sbt
I tried all of the outcommented lines, but I get fails because of the tests inserts of other tests inmemory database (like they run paralell). |
@tg44 Hmm, I'm not sure, it works fine on my code :s |
Of course I tried :D no positive result... 1-2 test always mess up others... |
I've got the same problem and I am failing to work around it.. |
@dotta I see that there are commits being made to master and I understand that everyone is busy but do you think there is any way to put the resolution of this bug on the roadmap along with the upgrade to play2.5 at least? If I had the knowledge to investigate I would. |
@dotta This also happens on version 2.0.0-M1 with Play 2.5 as well. |
I have finally managed to WORKAROUND this by using Play's full DI, without having pretty much any objects lying around |
@cipacda Can you provide some code examples please? Since there's Play 2.5 release, using app as implicit parameter like in the computer-database sample is no longer an option. |
@cipacda any code samples by any chance? My workaround seems to have broken between 2.4 and 2.5 |
My controllers looks like this:
and I manually create the fake app like this:
so a new database is used for each test. Since I've changed to this structure I didn't hit the problem anymore. Hope that helps! |
@cipacda I tried to replicate your pattern but I am getting this:
I followed typical DI pattern other than making a similar Thanks! |
I fixed it with a pattern similar to @cipacda. My pattern is as follows. I have a query trait that I mix in to any controllers that require database actions. "Children" of Query use this local trait Query {
val dbConfigProvider: DatabaseConfigProvider
...
} When mixing this trait into controllers, I do: class WhateverController @Inject()(actorSystem: ActorSystem, val dbConfigProvider: DatabaseConfigProvider) with Query And if I were to test against the class QuerySpec extends PlaySpec with OneServerPerSuite with Query {
val dbConfigProvider = app.injector.instanceOf(classOf[DatabaseConfigProvider])
val dbConfig = dbConfigProvider.get[JdbcProfile]
} I can run all of my tests without any problems now. I hope this can help someone! 👍 |
As noted in playframework/playframework#5466 there is a current issue when unit testing APIs that use Slick
For a minimal repro case, use "activator test" in this project
test.zip
The text was updated successfully, but these errors were encountered: