Search code examples
scalaunit-testingspring-bootmockingspy

Spying a method is calling the actual method instead of the mocked one


I have a service class say 'HostService'

 @Service
 class HostService {
       @Autowired
       private val platformService: PlatformService = null

       def views: Option[HostView] = {
             val ip = "10.x.x.x"
             if (!this.isReachable(ip))
                throw new IPNotFoundException
             else{
                var versionInfo: Option[String] = null
                versionInfo = platformService.contextPathOf("version") match  {
                   case versionInfo if (versionInfo == Some(null)) => Option("")
                   case versionInfo => versionInfo
                }
             }
       }

       def isReachable(ip:String) :Boolean = {
           // makes a URl connection and checks if IP is reachable
       }

Now I want to write a Unit Test case 'HostServiceTest' using Mockito. I will create a instance of host service and mock platform service and spy this host service instance to mock isReachable method.

    class HostServiceTest extends FlatSpec with Mockables with OptionValues with BeforeAndAfter {
         var platformService: PlatformService = _
         before {
             platformService = mock[PlatformService]                

when(platformService.contextPathOf((anyString()))).thenReturn(Option("1.0.0-SNAPSHOT"))
          }

        it should "return response with out error " in {
           val hostService: HostService = new HostService
           mockField(hostService, "platformService", platformService)
           val hostServiceSpy = spy(hostService)
           doReturn(true).when(hostServiceSpy ).isReachable(anyString())
           val data = hostService.views
           // Some validation Checks
        }

In the test case Instead of calling the mocked method of isReachable, It is going into the actual method.

I saw this question Mockito: Trying to spy on method is calling the original method and I did follow the way of stubbing they suggested and yet it is calling the actual one.o

What might be wrong in this?


Solution

  • When mocking the field, make sure it is 'var' if it is val the mocker cannot set the field to new value.

    Your HostService is having platformService declared as val(which means final)

    class HostService {
       @Autowired
       private val platformService: PlatformService = null
    

    As soon as you create hostService, it will have a final field platformService.

    val hostService: HostService = new HostService
    

    When you try mocking the platformService field it will mock API will tries to reset the platformService, which it could not as it is final.

    mockField(hostService, "platformService", platformService)
    

    Fix: Try changing the platformService to var

    private var platformService: PlatformService = null