AdamMc331
AdamMc331

Reputation: 16710

Custom JUnit Rule allows all tests to pass no matter what

I have a file that references some static methods:

class MyViewModel {

    fun test() { }

    companion object {
        private val MY_STRING = ResourceGrabber.grabString(R.string.blah_blah)
    }
}

In my JUnit test for this file, I write some code to mock my resource grabber in setup. This compiles and runs, and the following test fails as I'd expect it to:

@PrepareForTest(ResourceGrabber::class)
@RunWith(PowerMockRunner::class)
class MyViewModelTest {
    private lateinit var viewModel: MyViewModel

    @Before
    fun setup() {
        PowerMockito.mockStatic(ResourceGrabber::class.java)
        val mockResourceGrabber = Mockito.mock(ResourceGrabber::class.java)

        whenever(mockResourceGrabber.grabString(Mockito.anyInt())).thenAnswer { invocation ->
            val res: Int? = invocation?.arguments?.get(0) as? Int
            TestResourceGrabber.grabString(res)
        }

        viewModel = MyViewModel()
    }

    @Test
    fun someTest() {
        // Fails, as expected.
        assertEquals(2, 3)
    }
}

Here is where things get weird. I recently learned about custom JUnit rules that you can use to avoid some duplicated code across tests. In this case, I don't want to have to copy and paste my resource grabber work into every single test suite that uses it, so I made a custom rule:

class ResourceGrabberRule : TestRule {
    override fun apply(base: Statement?, description: Description?): Statement {
        return object : Statement() {
            override fun evaluate() {
                PowerMockito.mockStatic(ResourceGrabber::class.java)
                val mockResourceGrabber = Mockito.mock(ResourceGrabber::class.java)

                whenever(mockResourceGrabber.grabString(Mockito.anyInt())).thenAnswer { invocation ->
                    val res: Int? = invocation?.arguments?.get(0) as? Int
                    TestResourceGrabber.grabString(res)
                }
            }
        }
    }
}

Below is the implementation of that. The crazy thing is that now EVERY test is passing no matter what:

@PrepareForTest(ResourceGrabber::class)
@RunWith(PowerMockRunner::class)
class MyViewModelTest {
    private lateinit var viewModel: MyViewModel

    @Rule
    @JvmField
    val resourceGrabber = ResourceGrabberRule()

    @Before
    fun setup() {
        viewModel = MyViewModel()
    }

    @Test
    fun someTest() {
        // PASSES!!!?!?!?!?!
        assertEquals(2, 3)
    }
}

I'm not sure where the problem lies. I've tried building and running tests from both Android Studio and the command line. I don't know if I've implemented my rule incorrectly, or if it's an issue with the JUnit Rule connected with Powermock, or if it's an issue with Kotlin annotation processing. The tests compile and run but just pass no matter what's inside the tests themselves.

I'm open to comments about the architecture here (I'm sure the community has plenty) but I'm really looking for an explanation as to why the rule I wrote passes every test.

Upvotes: 5

Views: 692

Answers (1)

prfarlow
prfarlow

Reputation: 4381

In your custom TestRule, you need to call base.evaluate() to continue the chain of rules https://github.com/junit-team/junit4/wiki/rules#custom-rules

Upvotes: 2

Related Questions