nhoxbypass
nhoxbypass

Reputation: 10152

Write unit tests for private method of Presenter

I'm writing unit tests for Presenters of an MVP project.

class SplashPresenter {
    override fun onAttach() {
        if (dataManager.getAppLaunchFirstTime()) {
            onAppOpenFirstTime()
        } else {
            // Other logic...
        }
    }

    @VisibleForTesting
    fun onAppOpenFirstTime() {
        dataManager.setAppLaunchFirstTime(false)
        splashView.openLoginScreen()

        // May be I will add more functionalities in the future...
    }
}

Here we have 2 approaches to write unit test.

First approach

Verify directly what will happen: openLoginScreen() & setAppLaunchFirstTime(false). Don't care how they will be called.

@Test
fun onAttachTest_appLaunchFirstTime() {
    Mockito.`when`(dataManager.getAppLaunchFirstTime()).thenReturn(true)

    splashPresenter.onAttach()

    verify(dataManager).setAppLaunchFirstTime(false)
    verify(splashView).openLoginScreen()
}

Second approach

We use spy() to verify private internal method onAppOpenFirstTime() will be called. Then write another separate test for onAppOpenFirstTime().

@Test
fun onAttachTest_appLaunchFirstTime() {
    `when`(dataManager.getAppLaunchFirstTime()).thenReturn(true)
    val spyPresenter = Mockito.spy(splashPresenter)

    spyPresenter.onAttach()

    verify(spyPresenter).onAppOpenFirstTime()
}

@Test
fun onAppOpenFirstTimeTest() {
    splashPresenter.onAppOpenFirstTime()

    verify(dataManager).setAppLaunchFirstTime(false)
    verify(splashView).openLoginScreen()
}

So which approach is better? Which approach will make the project more testable when extending feature in the future?

Do we need to write unit test for private internal method?

Upvotes: 1

Views: 427

Answers (2)

Dirk Herrmann
Dirk Herrmann

Reputation: 5939

(You have asked which approach will make the project more testable, but testability is a property of the system under test. The distinguishing property of the test suites in this example, however, is the effort for test code maintenance.).

In your particular example, the second approach makes your tests unnecessarily dependent on implementation details which are likely to change: Chances are you rename onAppOpenFirstTime, split it up into more helper functions or even remove it and inline it into onAttach. In all these change scenarios the second approach will cause additional maintenance effort to keep the test suite working.

However, I emphasized that the dependency on onAppOpenFirstTime is unnecessary here. This is because there is no benefit in taking the second approach: The first approach, seems capable of finding the same bugs as the second, the tests in the first approach are as easy to set up as in the second (even easier) and so on.


If you only want to know how to solve the particular problem described, you can stop reading here. However, in other circumstances the situation may differ. I am adding the remarks below because I am not an advocate of the frequently stated mantra "don't test private methods".

Attempts to keep unit-test suites completely independent of implementation details is likely to result in inefficient test suites - that is, test suites that are not suited to find all bugs that could be found. And, finding bugs is one primary goal of testing (see Myers, Badgett, Sandler: The Art of Software Testing, or, Beizer: Software Testing Techniques, and many others).

Bugs are, in the end, in the implementation. Different implementations will have different bugs. Thus, trying to have an implementation agnostic test suite will likely fail to find bugs.

Think about the different ways to implement a Fibonacci function: as iterative/recursive function, closed form expression (Moivre/Binet), lookup table: Every implementation brings different potential bugs. Unit-testing is the testing method at the bottom of the test pyramid, and all higher-level tests (integration or system test) are less suited to find bugs in the implementation details.

The best approach therefore is to have as many as possible useful unit-tests that are implementation independent. Additionally, you will likely need additional unit-tests that aim at finding the potential bugs in the chosen implementation. In the end your test suite will likely be a mixture of both: Implementation independent tests, and tests that are implementation specific.

Implementation dependent tests will be more maintenance intensive, therefore for each one you should have a reason, for example to detect an implementation specific potential bug. Implementation specific tests are not inherently bad, but don't use them unnecessarily. And, the less stable an implementation aspect is, the more you should avoid making your tests dependent on it.

See Meszaros Principles of Test Automation: Ensure Commensurate Effort

Upvotes: 1

shubhamgarg1
shubhamgarg1

Reputation: 1995

The common wisdom is that you shouldn't test private methods. As your private methods would be called by some public methods, you should only test out the public ones.

You test a class's functionality and not methods individually. It would never be possible for you to test all methods in a class individually but you can always try to test the functionalities of a class(by using public methods) which will eventually lead you to call all methods of the class whether private or public thereby achieving a high code coverage.

First approach seems to be better as it tests what you want to achieve.

Upvotes: 0

Related Questions