kotlinfixture icon indicating copy to clipboard operation
kotlinfixture copied to clipboard

Benefits to creating a top-level generator?

Open romrell4 opened this issue 11 months ago • 2 comments

Hey @mattmook - love the library. Thanks for all the work you put into it. I'm proposing that we use it at Walmart to support our tests/sample apps. There's a pretty rigorous approval process before Walmart allows us to pull in external dependencies, especially those created and maintained by an individual. So before I propose the library to the powers-that-be, I'd love to make sure I completely understand how to best use the library.

So in our code, we often create "factory" functions for domain classes that provide base-level defaults, but support injecting whatever data is necessary. It might look something like this: in src/main/java...

data class Foo(
  val bar1: String,
  val bar2: Int,
  val bar3: List<SomeObj>,
  val bar4: String?,
)

in src/test/java/Fixtures.kt

fun createFoo(bar1: String = "", bar2: Int = 0, bar3: List<SomeObj> = listOf(), bar4: String? = null) = Foo(bar1, bar2, bar3, bar4)

and would be used in a test like

@Test
fun `test that the result is true when when bar2 is negative`() {
  val result = viewModel.doSomething(createFoo(bar2 = -1))
  assertThat(result).isTrue()
}

Obviously, the downside of this is that anytime a new variable gets added to Foo, the factory function has to be updated to support the new default value as well. We also work in a massively-multi-module-monolith, so it means that each module that depends on that Foo class would have to create it's own generator (we're looking at options for sharing test-time code, but that's aside from my current investigation) - in which case many generators would have to be updated to support the one new variable.

Alright, so there's some background. That all being said, we'd love to replace our factory function with your library. However, it seems you have an extra step, in that you create a top-level "Fixture", and then make multiple invocations on that generator. For instance, from your docs:

val fixture = kotlinFixture()

// Generate a list of strings
val aListOfStrings = fixture<List<String>>()

// Nulls are supported
val sometimesNull = fixture<Int?>()

// Create instances of classes
// Optional parameters will be randomly used or overridden
data class ADataClass(val value: String = "default")
val aClass = fixture<ADataClass>()

Are there memory or other reasons why you reuse your generator between invocations? Or would a helper function like:

inline fun <reified T> fixture() = kotlinFixture().invoke<T>()

work just as well? I ask because such a function would allow us to one-to-one replace our current fixture functions with your library. For instance, in the test above:

@Test
fun `test that the result is true when when bar2 is negative`() {
  val result = viewModel.doSomething(fixture<Foo>().copy(bar2 = -1))
  assertThat(result).isTrue()
}

I'd love to hear your thoughts. If such a function could be helpful to general public, I'm happy to submit a PR to contribute to the library. If you don't think so, I could keep that helper function just in Walmart's codebase.

Thanks in advance! And sorry if this isn't really an "issue" - just looked like the best place to start the discussion :)

romrell4 avatar Aug 01 '23 16:08 romrell4