paulbutcher / ScalaMock

Native Scala mocking framework
http://scalamock.org/
MIT License
502 stars 99 forks source link

Massive logs when mocked method relies on type class derivation #438

Closed vasigorc closed 2 years ago

vasigorc commented 2 years ago

ScalaMock Version 5.2.0

Scala Version 2.13.8

Runtime JVM

The issue (context)

We have a service similar to the one below:

trait ObservabilityService {
  def isObservabilityEnabled: Boolean
  def getObservabilityImplementation[Tracer, Meter](
    implicit ev: ImplementationTranslator[Tracer, Meter]
  ): ObservabilityImplementation[Tracer, Meter]
}

Where getObservabilityImplementation can return any implementation of any platform. The implementation translator looks like this:

sealed trait ImplementationTranslator[T, M] {
  def translate(config: Config): ObservabilityImplementation[T, M]
}

In the code we would use this as follows:

class OtelMetricsConsumer(observabilityService: ObservabilityService) extends Actor  {

  // implicit translator object
  import our.company.observability.ImplementationTranslators.OtelImplementationTranslator
  private val observabilityPlatform = observabilityService.getObservabilityImplementation

...and we would test it as follows:

class OtelMetricsConsumerSpec extends Specification with IsolatedMockFactory with AfterEach with BeforeEach {

   private val observabilityServiceMock: ObservabilityService = mock[ObservabilityService]
   private val observabilityImplementation: ObservabilityImplementation[OtelTracer, OtelMeter] =
    mock[ObservabilityImplementation[OtelTracer, OtelMeter]]

    override def before: Unit = {
    (observabilityServiceMock
      .getObservabilityImplementation(_: ImplementationTranslator[OtelTracer, OtelMeter]))
      .expects(*)
      .returning(observabilityImplementation)
      ...

Actual behaviour

Tests pass but there's numerous logs that get printed, similar to this:

  <mock-2> ObservabilityService.getObservabilityImplementation[Tracer,Meter](our.coompany.observability.ImplementationTranslators$OtelImplementationTranslator$@5e33a13b)
  <mock-2> ObservabilityService.getObservabilityImplementation[Tracer,Meter](our.coompany.observability.ImplementationTranslators$OtelImplementationTranslator$@5e33a13b)
  <mock-2> ObservabilityService.getObservabilityImplementation[Tracer,Meter](our.coompany.observability.ImplementationTranslators$OtelImplementationTranslator$@5e33a13b)
  <mock-2> ObservabilityService.getObservabilityImplementation[Tracer,Meter](our.coompany.observability.ImplementationTranslators$OtelImplementationTranslator$@5e33a13b)

to the extent that a build on our Jenkins server cause host to run out of space.

Note that removing the implicit modifier and passing a translator explicitly gets rid of the logs.

Expected behaviour

Regardless whether derivation is done via an implicit or explicit parameter these excessive logs should not occur.

vasigorc commented 2 years ago

Closing this issue. While there could be an issue creating mocks relying on type class derivation, the reason for excessive logs was because this creation was throwing an exception and thus killing the actor hosting it. After that the parent Akka actor was constantly trying to restart the killed actor and the loop continued...

To correct the issue I refactored the code to encapsulate the derivation part in the wrapping service.