1

I read this answer about unit-test a wrapper method. In following code:

public static Object methodA(Object arg) {
    if (arg == null) {
        return null;
    } else {
        return methodB();
    }
}

It looks reasonable that I don't need to test all functionality in methodB(), just test 2 cases where arg is null and not because methodB() should be tested already.

However, if methodB() is private method, I should test all functionality that methodB() will provide because according to another answer, private methods are implementation details.

The problem is, if I have 2 methods methodA1() and methodA2() and they all call methodB() like this:

public static MyObject methodA1(int x) {
    MyObject obj = new MyObject();
    obj.setX(x);
    return methodB(obj);
}

public static MyObject methodA2(int y) {
    MyObject obj = new MyObject();
    obj.setY(y);
    return methodB(obj);
}

private static MyObject methodB(MyObject obj) {
    // doSomething with obj
    return obj;
}

Should I test methodA1() and methodA2() separately? Or I can just test the private method methodB() because methodA1() and methodA2() are just wrapper methods of methodB(), so if methodB() is correctly tested, I won't need to test methodA1() and methodA2() at all.

EDIT:

I wrote separate tests for public methods at first. But the problem is, if there are many variations of the methodA, and some of the functionalities / requirements are shared in some of them, then the code of the test cases will be duplicated. That's why I wonder if I should test private method methodB().

The real problem I met is I have requirement to add a record in database, and there are many different APIs I should provide. For example, I should provide:

MyObject addMale(String name, String job);     // fill sex with "Male"
MyObject addStudent(String name, String sex);  // fill job with "Student"

And all of them checks the parameter is valid or not, fill the field that is not specified and call the private method to actually insert the record into the database, which is so called methodB().

There are many APIs like this, so if I can only test methodB() with all fields situation, maybe I can reduce the duplication of the test cases, but is that a good practice to do the unit tests like this?

EDIT2:

I know I can test private methods because I used reflection in my other test cases, and I know it can invoke private methods, too. But my question is about in this situation, testing private methods is a proper solution or not.

Community
  • 1
  • 1
Nier
  • 638
  • 7
  • 15
  • If `methodA1` does something different than `methodA2`, then it clearly needs a separate test. If both methods do the same thing, you'd only need the one method. – AJNeufeld Apr 11 '16 at 04:04
  • Expanding a bit: `methodA1` calls **`setX`** on an object and passes it to `methodB`, where as `methodA2` calls **`setY`** on an object and passes it to `methodB`. The behaviour of `methodB` will be different depending on whether the object's `X` is set, or its `Y` is set. Both need to be tested. If you just tested `methodB`, you'd need to write test cases for both cases anyway. Might as well make it clear by writing the test case for both `A1` and `A2`. – AJNeufeld Apr 11 '16 at 04:11
  • Running a code-coverage analysis of your unit test would show greater coverage if you ran unit tests on `methodA1` and `methodA2`, instead of just on `methodB`. – AJNeufeld Apr 11 '16 at 04:16
  • Because this is a simplified question, the real problem I met is that `methodA` has about 6 variation methods (or we can say it's flexible for extended), which uses different check conditions on input parameters or set different values on MyObject `obj`. The original thought is that I should test every method but the test cases are almost duplicated because the parameters are so familiar. That's why I think maybe only test `methodB` may be a good way, and just test `methodA`s for those checks. – Nier Apr 11 '16 at 04:25
  • @Nier is the behavior in `methodB` cohesive with the rest of the class or couldn't it have its own class and be made public ? Methods that have multiple consumers inside their own class are often full fledged objects in disguise. – guillaume31 Apr 11 '16 at 13:05
  • The fact that you have multiple delegates to the wrapped method makes no difference. That they delegate is an implementation details. – Raedwald Apr 11 '16 at 17:24
  • 1
    You have tagged this as TDD, yet the fact you are asking how to test code you have already written indicates you are not writing you tests first. – Raedwald Apr 11 '16 at 17:31
  • @guillaume31 Because `methodB()` is not in the requirement API, it just extracted common method from `methodA`s, so I make it private because it should not be used outside of the class, and I didn't write any parameter checks in it. – Nier Apr 12 '16 at 00:49
  • @Raedwald Actually I wrote test cases first when at the beginning `methodA` is only about 2 or 3 variations, and the requirement changed that I should provide more APIs which made me copy & paste the existing test case code to new one, which makes me feel uncomfortable, and that's why I'm wondering if I'm doing the right thing or not. And could you describe more detail about the first comment "The fact that you have multiple delegates to the wrapped method makes no difference." please? I'm not quite sure what does that means. – Nier Apr 12 '16 at 00:52
  • I know I can test private methods with some techniques, but I'm not sure if that's a proper solution to my situation since I found others said that private methods should not be tested. – Nier Apr 12 '16 at 00:59
  • @Nier you only seem to care about duplication in your test code while you should primarily aim at reducing it in your production code. `"There are many variations of the methodA"` is a **huge design smell**. Your inability to test things properly is just a side effect of that design issue. – guillaume31 Apr 12 '16 at 09:48
  • @guillaume31 I think you are right. I should discuss with the designer of the Use Case about this. I know there's something wrong when `methodA` comes with too many variations, but I dismissed it just because my requirement says so. I learn so many things and every answer points out their useful opinion but it turns out that my question is not good because I should rethink my design instead of test the bad production code. Should I delete this question or just leave it as it is? – Nier Apr 12 '16 at 09:59

3 Answers3

1

This is actually a question I've wondered about for awhile, and while it's not necessarily correct, I'll share my opinion.

The main problem is that private methods are hard to test. I did some Googling, and there some tools and techniques that let you access private methods (reflection is the main one I encountered), but they seemed a little convoluted.

But the reason you wrote a private method is because there is another method, a public method, that calls it. So while you can't directly test the private method, you can check its functionality by testing the public method which calls it.

If I were you, I would extensively test methodA1() and methodA2(), ensuring that all the functionality of methodB() is tested by the tests you run on the public methods.

Andy
  • 151
  • 7
  • If there's a functionality in `methodB()` that is shared between `methodA1()` and `methodA2()`, where should I test it? If I test it in both, my test cases will be duplicated, and if I test it in one of them, it becomes hard to maintain because such test may appear in any of the test cases. – Nier Apr 11 '16 at 05:17
  • 1
    Imho, you should test `methodA1()` and `methodA2()` *completely* (meaning with any input that is likely to go in there - what is "likely" depends on the method and usage, if it's purely internal, it might be less than if it's some kind of public API). Testing only 50% of `methodA2()` (because the other 50% are the same as in `methodA1()`) *can* be fatal - or can you ensure that nothing ever changes in your code? As soon as `methodA2()` changes, suddenly it might be 50% untested - and buggy. If you have separate unit tests, at least those will ensure that it still works as before. – Florian Schaetz Apr 11 '16 at 05:54
  • @FlorianSchaetz Thanks for the opinion. Did you see the last paragraph that I added in the last edit? What if I have multiple variations of `methodA()` and they shared a common base `methodB()`? I wrote many duplicate cases, for example: name is empty, name is not valid, name is null, job is empty... etc., and these duplicated code spread into many `methodA` test cases. If I can test them in `methodB()` I can reduce the duplicated code in test cases. In this situation, unit test a private `methodB()` is still considered a improper solution? – Nier Apr 11 '16 at 08:41
  • 1
    One way to minimize the duplicated code would be putting it, for example, into its own AssertJ assertion, so that you can call `MyCustomAssertion.assertThat( x ).isBasicallyCorrectFor("name", etc.);` Same thing is possible with Hamcrest matchers, so you could write one for something like `Assert.assertThat(x, isBasicallyCorrect("name", etc.));` But honestly, I think while test cases are code, too, duplication is not always as bad there as it would be in productive code. – Florian Schaetz Apr 11 '16 at 13:07
1

You've received a few opinions why you should write unit tests for methodA1() and methodA2(); you've pushed back on them. You asked the question here; you are clearly looking for some justification for not writing complete unit test for them. Let's see if we can give you the answer you want. ;-)

From what you've added in your edit, it looks like you have a variation on a builder pattern. Eg)

MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();

How would we test the builder?

Since the builder has 4 attributes, which could be set in any order, there would be 4! = 24 different orderings. A complete test suite would have to include:

@Test public void testXYZW() { ... }
@Test public void testXYWZ() { ... }
// ... 21 more permutations ...
@Test public void testWZYX() { ... }

But is that all? No! Some of those attributes could have default values, so we would have to test those patterns as well. Total orderings is now P(4,4)+P(4,3)+P(4,2)+P(4,1)+P(4,0) = 24+24+12+4+1 = 85 unit test.

@Test public void testXWY() { ... }
@Test public void testWY() { ... }
@Test public void testZ() { ... }
// ... 61 more permutations ordering

And this only tests each permutation of X, Y, Z, W attributes with one test value per attribute per test. It is clearly unwieldy to write an exhaustive set of tests for every possible combination and permutation.

The designer of the builder class would understand that the permutation of the attribute settings does not affect the resulting construction; writing tests for permutations of the order does not actually increase the test coverage. Tests for omitted attributes are useful, as they test the default values. Testing of different combinations of the omitted attributes again would not increase the test coverage. So, after careful thought, only two tests might be required:

@Test
public void testXYZW() {
    MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build();
    assertThat(o.wasBuiltProperly());
}

@Test void testDefaults() {
    MyObject o = new Builder().build();
    assertThat(o.wasBuiltProperlyFromDefaults());
}

If proper, complete testing of methodB() is being done, then you could safely get away with testing only the validation of inputs in methodA1() and methodA2().

@Test void testMethodA1Professor() {
    MyObject o = methodA1("professor");
    assertThat(o.wasBuiltProperlyWithProfessor());
}

@Test void testMethodA1WithNull() {
    MyObject o = methodA1(null);
    assertThat(o.wasBuiltProperlyWithNull());
}
AJNeufeld
  • 8,526
  • 1
  • 25
  • 44
0

Oftentimes, when a private method is used by multiple methods inside a class, there's a distinct concept lurking behind it. It may deserve to have its own class.

it should not be used outside of the class

There are a few ways you could extract methodB to its own class and not make it available as part of your public API :

  • Nest it inside the class that uses it and give it a restricted scope

  • Put it in another module at a lower level. Make that module available from your API but not from the clients.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • Can you give more detail information about why `methodB` deserves its own class? Because `methodB` works fine if it's just a private method, I can't figure out what's the benefit of moving it to another class, which seems only reduces the readability. – Nier Apr 12 '16 at 08:26
  • Note the words *oftentimes* and *may* in my answer. I don't affirm that it deserves its own class. Only you have the knowledge to tell. – guillaume31 Apr 12 '16 at 09:32
  • It's not a matter of working fine or not working fine, it's a matter of cohesion in the class. The [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle) tells us that if your class has two reasons to change because `methodB` is independent enough from the rest, then it should be split in two. 1 class = 1 concept. 2 concepts = 2 classes. – guillaume31 Apr 12 '16 at 09:39