edvin / tornadofx

Lightweight JavaFX Framework for Kotlin
Apache License 2.0
3.67k stars 269 forks source link

How to influence inject during testing #596

Open ronsmits opened 6 years ago

ronsmits commented 6 years ago

I am making this an issue so its easier to discuss I hope.

When writing tests you want to be able to mock parts of your application (databaseaccess, rest call etc) A normal pattern in tornadofx is to have database access and such hidden in a controller and the controller gets injected into the view:

class TestView : View("test view") {
   val controller : DbController by inject()
   override val root = tableview(controller.getAllCustomers()){
      column("name", Customer::name)
     column("id", Customer::id)
  }
}

How to override the DbController that gets inserted into the view? This will help if you want your mocked object to be made by either a seperate interface implementation or a mock object like this:

val controller : DbController = mockk() // in this case DbController is an mocked object

or

val controller : DbController = DbControllerTestImpl() // in this case DbController is an interface
edvin commented 6 years ago

The solution is to create a separate scope for the test, and inject a custom instance of DbController into that scope before you instantiate the view. The problem with this is that we don't support interfaces for controllers in a meaningful way. I think it should be fairly easy to implement this though.

ronsmits commented 6 years ago

and how do you inject that custom instance? I have been reading the code for find and the customInstance needs to adhere to the signature of the original controller. If find (in any scope) cannot find exactly that signature it will create it.

ronsmits commented 6 years ago

The problem goes deeper when you have a two stage injection(not uncommon) where the controller itself is dependent on another object and that object needs to be injected too

edvin commented 6 years ago

The problem doesn't really go deeper, you just have to inject a custom instance of the other controller as well. First we need to figure out how to support interfaces. I will look at this later today.

ronsmits commented 6 years ago

Take my first example val controller : DbController = mockk() how do you make inject return this? the FX.components map is an internal val you cannot manipulate it

edvin commented 6 years ago

You'd do scope.put(mock()), but we need support for instances, or you simply can't create different versions of a controller.

nhajratw commented 5 years ago

Did this support make its way in at any point recently?

edvin commented 5 years ago

No, it did not.