Skip to content
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

Benefits to creating a top-level generator? #98

Open
romrell4 opened this issue Aug 1, 2023 · 2 comments
Open

Benefits to creating a top-level generator? #98

romrell4 opened this issue Aug 1, 2023 · 2 comments

Comments

@romrell4
Copy link

romrell4 commented Aug 1, 2023

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
Copy link
Author

romrell4 commented Aug 1, 2023

From my investigation, it looks like the Fixture class that is returned from kotlinFixture is a very lightweight class that doesn't retain any state outside of it's configuration. So in the case that a user doesn't require a top-level configuration, would it be helpful to support a:

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

function that creates the mock in a one-liner, rather than making them go through the middle hoop?

@romrell4
Copy link
Author

romrell4 commented Aug 1, 2023

In case you do like the idea, here's a quick PR for it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant