As others have pointed out, the semantics of the function parameter matter. The tests for
funArg are two completely separate tests, and are different kinds of tests at that.
funArg will ultimately be custom code provided by users of
fun, you want to make sure that the abstract behavior of
funArg is what
fun expects - which means that you have to consider what that abstract behavior is, and how to express, test & document it. The significance of
funArg is that it is an extension point for the logic in
fun, and as such, it is defined by not just the signature, but also by its semantics with respect to
Let me give you an example; suppose
fun does some logic that involves user-defined comparisons of
y. and that
funArg is the familiar1 comparison function
int compare(a, b).
1 It appears in various languages and libraries; the gist of it is:
int compare(a, b) returns:
Less than zero if a < b
Zero if a == b
Greater than zero if a > b
Suppose also that that
funArg to establish a well defined order.
That constrains the behavior of
compare; it has to be written a certain way for
fun to work, as the code in
fun makes certain assumptions.
E.g. it has to follow these rules (say):
- multiple calls to
compare(a, b) have to return consistent results
compare(a, a) must return 0
compare(a, b) == compare(b, a), then it must be that
compare(a, b) == 0, and that
a == b (by some compatible notion of equality).
compare(a, b) and
compare(b, a) cannot otherwise indicate the same ordering (e.g., they cannot both return negative, but different numbers; they have to have different signs).
compare(a, b) < 0 and
compare(b, c) < 0, then
compare(a, c) < 0 must also be true.
- etc., etc., depending on what you're trying to do
If this is what
fun expects from
funArg in order to do its job, than if a user supplies an implementation (a lambda) that violates some of these rules, then
fun will not work (or worse, it will appear to work for a while, and then produce a weird bug). The supplied function would break the Liskov substitution principle with respect to
fun. But that's on the caller, not on you.
You can write this set of bullet points as a set of tests that, by virtue of existing, (1) document this behavior, and (2) exercise the candidate lambda on some number of examples, thus providing some confidence that the implementation is correct.
I can of course add a unit test with some random
funArg, but as more people use the class, the more different functions they will pass as arguments.
Your job here is not to test these functions for them; your job is to write the test for the abstract
funArg, and make it accessible to people as part of the documentation of
fun (which must also document what
funArg is supposed to represent). That is, your job is to design the behavioral contract of
funArg with respect to
This is so that other people can plug in their own lambdas and run the test on their code, to check if it confirms to the specification of
fun (a library function they have chosen to use). The test for
funArg defines what kinds of extensions
You may write a
funArg implementation for your own use, or a test-only mock implementation, but the test you're writing here is a high-level test for pluggable code, so it has to allow for different lambdas to be plugged in (TDD frameworks aren't great at this, but it can be done).
Another complication is that, depending on the application, the specific values you used for your test cases may not work well for user-provided implementations; it's not an easy problem, and it may be that the best you can do is publish the
funArg tests as a reference that other people can use when writing their own test suite.
In any case, you may decide that you don't want to bother writing/publishing such a test - and that's fine; that's up to you (and your team), and depends on how critical it is to get the implementation right, how nuanced the expected behavior is, etc.
Unlike the other one, this is a test of code that is in your control. Here, in every test, you'd inject a different, test case–specific lambda. You're not trying to test
funArg here at all. You're testing what
fun will do given some completely predetermined behavior of
funArg that you decided on in order to facilitate the needs of that particular test case.
E.g., suppose you want to test that, if the input is such that
x < y, then
fun should return
y - x, continuing with the example above where
funArg is a user-supplied comparison.
Your test would then supply concrete
y values that make sense in the context of this premise, and for
funArg it would supply a lambda that returns a value indicating that
x is smaller than
y. Remember, you're not testing the lambda here. For the purposes of this test, it can be hardcoded to return -1, without ever looking at
y! It's a mock implementation, the sole purpose of which is to facilitate the test scenario.
So, within this group of tests, what other people who are going to use
fun might provide as
funArg is not of concern at all (beyond, you know, some sanity checks for things that may be beyond your control, like parameters resulting in a numerical overflow, or something that's a security concern, like input that needs to be sanitized, etc).