You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by mi...@apache.org on 2022/02/09 16:04:59 UTC

[kafka] branch trunk updated: KAFKA-13577: Replace easymock with mockito in kafka:core - part 1 (#11672)

This is an automated email from the ASF dual-hosted git repository.

mimaison pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 150fecf  KAFKA-13577: Replace easymock with mockito in kafka:core - part 1 (#11672)
150fecf is described below

commit 150fecf6d3a1be30eb1b2f40a27524a9f60586df
Author: Mickael Maison <mi...@users.noreply.github.com>
AuthorDate: Wed Feb 9 17:02:27 2022 +0100

    KAFKA-13577: Replace easymock with mockito in kafka:core - part 1 (#11672)
    
    
    Reviewers: Tom Bentley <tb...@redhat.com>
---
 .../kafka/server/DelayedFetchTest.scala            |  43 +-
 .../kafka/common/InterBrokerSendThreadTest.scala   | 157 ++--
 .../scala/unit/kafka/admin/ConfigCommandTest.scala | 126 ++-
 .../controller/PartitionStateMachineTest.scala     | 214 +++--
 .../kafka/controller/ReplicaStateMachineTest.scala |  77 +-
 .../AbstractCoordinatorConcurrencyTest.scala       |  10 +-
 .../group/GroupCoordinatorConcurrencyTest.scala    |   8 +-
 .../coordinator/group/GroupCoordinatorTest.scala   | 449 +++--------
 .../group/GroupMetadataManagerTest.scala           | 530 +++++++------
 .../transaction/ProducerIdManagerTest.scala        |  39 +-
 .../TransactionCoordinatorConcurrencyTest.scala    |  77 +-
 .../transaction/TransactionCoordinatorTest.scala   | 858 ++++++++++-----------
 .../TransactionMarkerChannelManagerTest.scala      | 318 ++++----
 ...sactionMarkerRequestCompletionHandlerTest.scala |  65 +-
 .../transaction/TransactionStateManagerTest.scala  | 256 +++---
 15 files changed, 1440 insertions(+), 1787 deletions(-)

diff --git a/core/src/test/scala/integration/kafka/server/DelayedFetchTest.scala b/core/src/test/scala/integration/kafka/server/DelayedFetchTest.scala
index 7585862..581af29 100644
--- a/core/src/test/scala/integration/kafka/server/DelayedFetchTest.scala
+++ b/core/src/test/scala/integration/kafka/server/DelayedFetchTest.scala
@@ -17,7 +17,6 @@
 package kafka.server
 
 import java.util.Optional
-
 import scala.collection.Seq
 import kafka.cluster.Partition
 import kafka.log.LogOffsetSnapshot
@@ -27,11 +26,12 @@ import org.apache.kafka.common.message.OffsetForLeaderEpochResponseData.EpochEnd
 import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.record.MemoryRecords
 import org.apache.kafka.common.requests.FetchRequest
-import org.easymock.{EasyMock, EasyMockSupport}
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.Assertions._
+import org.mockito.ArgumentMatchers.{any, anyInt}
+import org.mockito.Mockito.{mock, when}
 
-class DelayedFetchTest extends EasyMockSupport {
+class DelayedFetchTest {
   private val maxBytes = 1024
   private val replicaManager: ReplicaManager = mock(classOf[ReplicaManager])
   private val replicaQuota: ReplicaQuota = mock(classOf[ReplicaQuota])
@@ -64,18 +64,16 @@ class DelayedFetchTest extends EasyMockSupport {
 
     val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.expect(replicaManager.getPartitionOrException(topicIdPartition.topicPartition))
-        .andReturn(partition)
-    EasyMock.expect(partition.fetchOffsetSnapshot(
+    when(replicaManager.getPartitionOrException(topicIdPartition.topicPartition))
+        .thenReturn(partition)
+    when(partition.fetchOffsetSnapshot(
         currentLeaderEpoch,
         fetchOnlyFromLeader = true))
-        .andThrow(new FencedLeaderEpochException("Requested epoch has been fenced"))
-    EasyMock.expect(replicaManager.isAddingReplica(EasyMock.anyObject(), EasyMock.anyInt())).andReturn(false)
+        .thenThrow(new FencedLeaderEpochException("Requested epoch has been fenced"))
+    when(replicaManager.isAddingReplica(any(), anyInt())).thenReturn(false)
 
     expectReadFromReplica(replicaId, topicIdPartition, fetchStatus.fetchInfo, Errors.FENCED_LEADER_EPOCH)
 
-    replayAll()
-
     assertTrue(delayedFetch.tryComplete())
     assertTrue(delayedFetch.isCompleted)
     assertTrue(fetchResultOpt.isDefined)
@@ -110,12 +108,10 @@ class DelayedFetchTest extends EasyMockSupport {
       clientMetadata = None,
       responseCallback = callback)
 
-    EasyMock.expect(replicaManager.getPartitionOrException(topicIdPartition.topicPartition))
-      .andThrow(new NotLeaderOrFollowerException(s"Replica for $topicIdPartition not available"))
+    when(replicaManager.getPartitionOrException(topicIdPartition.topicPartition))
+      .thenThrow(new NotLeaderOrFollowerException(s"Replica for $topicIdPartition not available"))
     expectReadFromReplica(replicaId, topicIdPartition, fetchStatus.fetchInfo, Errors.NOT_LEADER_OR_FOLLOWER)
-    EasyMock.expect(replicaManager.isAddingReplica(EasyMock.anyObject(), EasyMock.anyInt())).andReturn(false)
-
-    replayAll()
+    when(replicaManager.isAddingReplica(any(), anyInt())).thenReturn(false)
 
     assertTrue(delayedFetch.tryComplete())
     assertTrue(delayedFetch.isCompleted)
@@ -150,21 +146,20 @@ class DelayedFetchTest extends EasyMockSupport {
       responseCallback = callback)
 
     val partition: Partition = mock(classOf[Partition])
-    EasyMock.expect(replicaManager.getPartitionOrException(topicIdPartition.topicPartition)).andReturn(partition)
+    when(replicaManager.getPartitionOrException(topicIdPartition.topicPartition)).thenReturn(partition)
     val endOffsetMetadata = LogOffsetMetadata(messageOffset = 500L, segmentBaseOffset = 0L, relativePositionInSegment = 500)
-    EasyMock.expect(partition.fetchOffsetSnapshot(
+    when(partition.fetchOffsetSnapshot(
       currentLeaderEpoch,
       fetchOnlyFromLeader = true))
-      .andReturn(LogOffsetSnapshot(0L, endOffsetMetadata, endOffsetMetadata, endOffsetMetadata))
-    EasyMock.expect(partition.lastOffsetForLeaderEpoch(currentLeaderEpoch, lastFetchedEpoch.get, fetchOnlyFromLeader = false))
-      .andReturn(new EpochEndOffset()
+      .thenReturn(LogOffsetSnapshot(0L, endOffsetMetadata, endOffsetMetadata, endOffsetMetadata))
+    when(partition.lastOffsetForLeaderEpoch(currentLeaderEpoch, lastFetchedEpoch.get, fetchOnlyFromLeader = false))
+      .thenReturn(new EpochEndOffset()
         .setPartition(topicIdPartition.partition)
         .setErrorCode(Errors.NONE.code)
         .setLeaderEpoch(lastFetchedEpoch.get)
         .setEndOffset(fetchOffset - 1))
-    EasyMock.expect(replicaManager.isAddingReplica(EasyMock.anyObject(), EasyMock.anyInt())).andReturn(false)
+    when(replicaManager.isAddingReplica(any(), anyInt())).thenReturn(false)
     expectReadFromReplica(replicaId, topicIdPartition, fetchStatus.fetchInfo, Errors.NONE)
-    replayAll()
 
     assertTrue(delayedFetch.tryComplete())
     assertTrue(delayedFetch.isCompleted)
@@ -188,7 +183,7 @@ class DelayedFetchTest extends EasyMockSupport {
                                     topicIdPartition: TopicIdPartition,
                                     fetchPartitionData: FetchRequest.PartitionData,
                                     error: Errors): Unit = {
-    EasyMock.expect(replicaManager.readFromLocalLog(
+    when(replicaManager.readFromLocalLog(
       replicaId = replicaId,
       fetchOnlyFromLeader = true,
       fetchIsolation = FetchLogEnd,
@@ -197,7 +192,7 @@ class DelayedFetchTest extends EasyMockSupport {
       readPartitionInfo = Seq((topicIdPartition, fetchPartitionData)),
       clientMetadata = None,
       quota = replicaQuota))
-      .andReturn(Seq((topicIdPartition, buildReadResult(error))))
+      .thenReturn(Seq((topicIdPartition, buildReadResult(error))))
   }
 
   private def buildReadResult(error: Errors): LogReadResult = {
diff --git a/core/src/test/scala/kafka/common/InterBrokerSendThreadTest.scala b/core/src/test/scala/kafka/common/InterBrokerSendThreadTest.scala
index f2d9fb9..da9cb63 100644
--- a/core/src/test/scala/kafka/common/InterBrokerSendThreadTest.scala
+++ b/core/src/test/scala/kafka/common/InterBrokerSendThreadTest.scala
@@ -22,17 +22,18 @@ import org.apache.kafka.common.Node
 import org.apache.kafka.common.errors.{AuthenticationException, DisconnectException}
 import org.apache.kafka.common.protocol.ApiKeys
 import org.apache.kafka.common.requests.AbstractRequest
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
-import org.mockito.{ArgumentMatchers, Mockito}
+import org.mockito.ArgumentMatchers.{any, anyLong, same}
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.{mock, verify, when}
 
 import java.util
 import scala.collection.mutable
 
 class InterBrokerSendThreadTest {
   private val time = new MockTime()
-  private val networkClient: NetworkClient = EasyMock.createMock(classOf[NetworkClient])
+  private val networkClient: NetworkClient = mock(classOf[NetworkClient])
   private val completionHandler = new StubCompletionHandler
   private val requestTimeoutMs = 1000
 
@@ -63,14 +64,15 @@ class InterBrokerSendThreadTest {
 
   @Test
   def shutdownThreadShouldNotCauseException(): Unit = {
-    val networkClient = Mockito.mock(classOf[NetworkClient])
     // InterBrokerSendThread#shutdown calls NetworkClient#initiateClose first so NetworkClient#poll
     // can throw DisconnectException when thread is running
-    Mockito.when(networkClient.poll(ArgumentMatchers.anyLong, ArgumentMatchers.anyLong)).thenThrow(new DisconnectException())
+    when(networkClient.poll(anyLong(), anyLong())).thenThrow(new DisconnectException())
     var exception: Throwable = null
     val thread = new TestInterBrokerSendThread(networkClient, e => exception = e)
     thread.shutdown()
     thread.pollOnce(100)
+
+    verify(networkClient).poll(anyLong(), anyLong())
     assertNull(exception)
   }
 
@@ -79,14 +81,12 @@ class InterBrokerSendThreadTest {
     val sendThread = new TestInterBrokerSendThread()
 
     // poll is always called but there should be no further invocations on NetworkClient
-    EasyMock.expect(networkClient.poll(EasyMock.anyLong(), EasyMock.anyLong()))
-      .andReturn(new util.ArrayList())
-
-    EasyMock.replay(networkClient)
+    when(networkClient.poll(anyLong(), anyLong()))
+      .thenReturn(new util.ArrayList[ClientResponse]())
 
     sendThread.doWork()
 
-    EasyMock.verify(networkClient)
+    verify(networkClient).poll(anyLong(), anyLong())
     assertFalse(completionHandler.executedWithDisconnectedResponse)
   }
 
@@ -99,29 +99,34 @@ class InterBrokerSendThreadTest {
 
     val clientRequest = new ClientRequest("dest", request, 0, "1", 0, true, requestTimeoutMs, handler.handler)
 
-    EasyMock.expect(networkClient.newClientRequest(
-      EasyMock.eq("1"),
-      EasyMock.same(handler.request),
-      EasyMock.anyLong(),
-      EasyMock.eq(true),
-      EasyMock.eq(requestTimeoutMs),
-      EasyMock.same(handler.handler)))
-      .andReturn(clientRequest)
-
-    EasyMock.expect(networkClient.ready(node, time.milliseconds()))
-      .andReturn(true)
-
-    EasyMock.expect(networkClient.send(clientRequest, time.milliseconds()))
+    when(networkClient.newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      anyLong(),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler)))
+      .thenReturn(clientRequest)
 
-    EasyMock.expect(networkClient.poll(EasyMock.anyLong(), EasyMock.anyLong()))
-      .andReturn(new util.ArrayList())
+    when(networkClient.ready(node, time.milliseconds()))
+      .thenReturn(true)
 
-    EasyMock.replay(networkClient)
+    when(networkClient.poll(anyLong(), anyLong()))
+      .thenReturn(new util.ArrayList[ClientResponse]())
 
     sendThread.enqueue(handler)
     sendThread.doWork()
 
-    EasyMock.verify(networkClient)
+    verify(networkClient).newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      anyLong(),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler))
+    verify(networkClient).ready(any[Node], anyLong())
+    verify(networkClient).send(same(clientRequest), anyLong())
+    verify(networkClient).poll(anyLong(), anyLong())
     assertFalse(completionHandler.executedWithDisconnectedResponse)
   }
 
@@ -134,36 +139,45 @@ class InterBrokerSendThreadTest {
 
     val clientRequest = new ClientRequest("dest", request, 0, "1", 0, true, requestTimeoutMs, handler.handler)
 
-    EasyMock.expect(networkClient.newClientRequest(
-      EasyMock.eq("1"),
-      EasyMock.same(handler.request),
-      EasyMock.anyLong(),
-      EasyMock.eq(true),
-      EasyMock.eq(requestTimeoutMs),
-      EasyMock.same(handler.handler)))
-      .andReturn(clientRequest)
+    when(networkClient.newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      anyLong(),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler)))
+      .thenReturn(clientRequest)
 
-    EasyMock.expect(networkClient.ready(node, time.milliseconds()))
-      .andReturn(false)
+    when(networkClient.ready(node, time.milliseconds()))
+      .thenReturn(false)
 
-    EasyMock.expect(networkClient.connectionDelay(EasyMock.anyObject(), EasyMock.anyLong()))
-      .andReturn(0)
+    when(networkClient.connectionDelay(any[Node], anyLong()))
+      .thenReturn(0)
 
-    EasyMock.expect(networkClient.poll(EasyMock.anyLong(), EasyMock.anyLong()))
-      .andReturn(new util.ArrayList())
+    when(networkClient.poll(anyLong(), anyLong()))
+      .thenReturn(new util.ArrayList[ClientResponse]())
 
-    EasyMock.expect(networkClient.connectionFailed(node))
-      .andReturn(true)
+   when(networkClient.connectionFailed(node))
+      .thenReturn(true)
 
-    EasyMock.expect(networkClient.authenticationException(node))
-      .andReturn(new AuthenticationException(""))
-
-    EasyMock.replay(networkClient)
+    when(networkClient.authenticationException(node))
+      .thenReturn(new AuthenticationException(""))
 
     sendThread.enqueue(handler)
     sendThread.doWork()
 
-    EasyMock.verify(networkClient)
+    verify(networkClient).newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      anyLong,
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler))
+    verify(networkClient).ready(any[Node], anyLong)
+    verify(networkClient).connectionDelay(any[Node], anyLong)
+    verify(networkClient).poll(anyLong, anyLong)
+    verify(networkClient).connectionFailed(any[Node])
+    verify(networkClient).authenticationException(any[Node])
     assertTrue(completionHandler.executedWithDisconnectedResponse)
   }
 
@@ -184,35 +198,44 @@ class InterBrokerSendThreadTest {
       handler.handler)
     time.sleep(1500)
 
-    EasyMock.expect(networkClient.newClientRequest(
-      EasyMock.eq("1"),
-      EasyMock.same(handler.request),
-      EasyMock.eq(handler.creationTimeMs),
-      EasyMock.eq(true),
-      EasyMock.eq(requestTimeoutMs),
-      EasyMock.same(handler.handler)))
-      .andReturn(clientRequest)
+    when(networkClient.newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      ArgumentMatchers.eq(handler.creationTimeMs),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler)))
+      .thenReturn(clientRequest)
 
     // make the node unready so the request is not cleared
-    EasyMock.expect(networkClient.ready(node, time.milliseconds()))
-      .andReturn(false)
+    when(networkClient.ready(node, time.milliseconds()))
+      .thenReturn(false)
 
-    EasyMock.expect(networkClient.connectionDelay(EasyMock.anyObject(), EasyMock.anyLong()))
-      .andReturn(0)
+    when(networkClient.connectionDelay(any[Node], anyLong()))
+      .thenReturn(0)
 
-    EasyMock.expect(networkClient.poll(EasyMock.anyLong(), EasyMock.anyLong()))
-      .andReturn(new util.ArrayList())
+    when(networkClient.poll(anyLong(), anyLong()))
+      .thenReturn(new util.ArrayList[ClientResponse]())
 
     // rule out disconnects so the request stays for the expiry check
-    EasyMock.expect(networkClient.connectionFailed(node))
-      .andReturn(false)
-
-    EasyMock.replay(networkClient)
+    when(networkClient.connectionFailed(node))
+      .thenReturn(false)
 
     sendThread.enqueue(handler)
     sendThread.doWork()
 
-    EasyMock.verify(networkClient)
+    verify(networkClient).newClientRequest(
+      ArgumentMatchers.eq("1"),
+      same(handler.request),
+      ArgumentMatchers.eq(handler.creationTimeMs),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(requestTimeoutMs),
+      same(handler.handler))
+    verify(networkClient).ready(any[Node], anyLong)
+    verify(networkClient).connectionDelay(any[Node], anyLong)
+    verify(networkClient).poll(anyLong, anyLong)
+    verify(networkClient).connectionFailed(any[Node])
+
     assertFalse(sendThread.hasUnsentRequests)
     assertTrue(completionHandler.executedWithDisconnectedResponse)
   }
diff --git a/core/src/test/scala/unit/kafka/admin/ConfigCommandTest.scala b/core/src/test/scala/unit/kafka/admin/ConfigCommandTest.scala
index 859b3d5..c7ccf82 100644
--- a/core/src/test/scala/unit/kafka/admin/ConfigCommandTest.scala
+++ b/core/src/test/scala/unit/kafka/admin/ConfigCommandTest.scala
@@ -35,9 +35,9 @@ import org.apache.kafka.common.security.auth.SecurityProtocol
 import org.apache.kafka.common.security.scram.internals.ScramCredentialUtils
 import org.apache.kafka.common.utils.Sanitizer
 import org.apache.kafka.test.TestUtils
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
+import org.mockito.Mockito.{mock, times, verify, when}
 
 import scala.collection.{Seq, mutable}
 import scala.jdk.CollectionConverters._
@@ -129,7 +129,7 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     try {
       ConfigCommand.main(args)
     } catch {
-      case e: RuntimeException =>
+      case _: RuntimeException =>
     } finally {
       Exit.resetExitProcedure()
     }
@@ -525,7 +525,7 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
   }
 
   private def verifyAlterCommandFails(expectedErrorMessage: String, alterOpts: Seq[String]): Unit = {
-    val mockAdminClient: Admin = EasyMock.createStrictMock(classOf[Admin])
+    val mockAdminClient: Admin = mock(classOf[Admin])
     val opts = new ConfigCommandOptions(Array("--bootstrap-server", "localhost:9092",
       "--alter") ++ alterOpts)
     val e = assertThrows(classOf[IllegalArgumentException], () => ConfigCommand.alterConfig(mockAdminClient, opts))
@@ -549,8 +549,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       "--describe") ++ describeArgs)
     val describeFuture = new KafkaFutureImpl[util.Map[ClientQuotaEntity, util.Map[String, java.lang.Double]]]
     describeFuture.complete(Map.empty[ClientQuotaEntity, util.Map[String, java.lang.Double]].asJava)
-    val describeResult: DescribeClientQuotasResult = EasyMock.createNiceMock(classOf[DescribeClientQuotasResult])
-    EasyMock.expect(describeResult.entities()).andReturn(describeFuture)
+    val describeResult: DescribeClientQuotasResult = mock(classOf[DescribeClientQuotasResult])
+    when(describeResult.entities()).thenReturn(describeFuture)
 
     var describedConfigs = false
     val node = new Node(1, "localhost", 9092)
@@ -562,7 +562,6 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         describeResult
       }
     }
-    EasyMock.replay(describeResult)
     ConfigCommand.describeConfig(mockAdminClient, describeOpts)
     assertTrue(describedConfigs)
   }
@@ -589,8 +588,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     var describedConfigs = false
     val describeFuture = new KafkaFutureImpl[util.Map[ClientQuotaEntity, util.Map[String, java.lang.Double]]]
     describeFuture.complete(Map(expectedAlterEntity -> expectedProps.asJava).asJava)
-    val describeResult: DescribeClientQuotasResult = EasyMock.createNiceMock(classOf[DescribeClientQuotasResult])
-    EasyMock.expect(describeResult.entities()).andReturn(describeFuture)
+    val describeResult: DescribeClientQuotasResult = mock(classOf[DescribeClientQuotasResult])
+    when(describeResult.entities()).thenReturn(describeFuture)
 
     val expectedFilterComponents = expectedAlterEntity.entries.asScala.map { case (entityType, entityName) =>
       if (entityName == null)
@@ -602,8 +601,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     var alteredConfigs = false
     val alterFuture = new KafkaFutureImpl[Void]
     alterFuture.complete(null)
-    val alterResult: AlterClientQuotasResult = EasyMock.createNiceMock(classOf[AlterClientQuotasResult])
-    EasyMock.expect(alterResult.all()).andReturn(alterFuture)
+    val alterResult: AlterClientQuotasResult = mock(classOf[AlterClientQuotasResult])
+    when(alterResult.all()).thenReturn(alterFuture)
 
     val node = new Node(1, "localhost", 9092)
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
@@ -625,7 +624,6 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         alterResult
       }
     }
-    EasyMock.replay(alterResult, describeResult)
     ConfigCommand.alterConfig(mockAdminClient, createOpts)
     assertTrue(describedConfigs)
     assertTrue(alteredConfigs)
@@ -741,10 +739,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       // User SCRAM credentials should not be described when specifying
       // --describe --entity-type users --entity-default (or --user-defaults) with --bootstrap-server
       val describeFuture = new KafkaFutureImpl[util.Map[ClientQuotaEntity, util.Map[String, java.lang.Double]]]
-      describeFuture.complete(Map((new ClientQuotaEntity(Map("" -> "").asJava) -> Map(("request_percentage" -> Double.box(50.0))).asJava)).asJava)
-      val describeClientQuotasResult: DescribeClientQuotasResult = EasyMock.createNiceMock(classOf[DescribeClientQuotasResult])
-      EasyMock.expect(describeClientQuotasResult.entities()).andReturn(describeFuture)
-      EasyMock.replay(describeClientQuotasResult)
+      describeFuture.complete(Map(new ClientQuotaEntity(Map("" -> "").asJava) -> Map("request_percentage" -> Double.box(50.0)).asJava).asJava)
+      val describeClientQuotasResult: DescribeClientQuotasResult = mock(classOf[DescribeClientQuotasResult])
+      when(describeClientQuotasResult.entities()).thenReturn(describeFuture)
       val node = new Node(1, "localhost", 9092)
       val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
         override def describeClientQuotas(filter: ClientQuotaFilter, options: DescribeClientQuotasOptions):  DescribeClientQuotasResult = {
@@ -823,13 +820,13 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val configEntries = List(newConfigEntry("min.insync.replicas", "1"), newConfigEntry("unclean.leader.election.enable", "1")).asJava
     val future = new KafkaFutureImpl[util.Map[ConfigResource, Config]]
     future.complete(util.Collections.singletonMap(resource, new Config(configEntries)))
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
-    EasyMock.expect(describeResult.all()).andReturn(future).once()
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
+    when(describeResult.all()).thenReturn(future)
 
     val alterFuture = new KafkaFutureImpl[Void]
     alterFuture.complete(null)
-    val alterResult: AlterConfigsResult = EasyMock.createNiceMock(classOf[AlterConfigsResult])
-    EasyMock.expect(alterResult.all()).andReturn(alterFuture)
+    val alterResult: AlterConfigsResult = mock(classOf[AlterConfigsResult])
+    when(alterResult.all()).thenReturn(alterFuture)
 
     val node = new Node(1, "localhost", 9092)
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
@@ -867,10 +864,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         alterResult
       }
     }
-    EasyMock.replay(alterResult, describeResult)
     ConfigCommand.alterConfig(mockAdminClient, alterOpts)
     assertTrue(alteredConfigs)
-    EasyMock.reset(alterResult, describeResult)
+    verify(describeResult).all()
   }
 
   @Test
@@ -885,8 +881,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val resource = new ConfigResource(ConfigResource.Type.TOPIC, resourceName)
     val future = new KafkaFutureImpl[util.Map[ConfigResource, Config]]
     future.complete(util.Collections.singletonMap(resource, new Config(util.Collections.emptyList[ConfigEntry])))
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
-    EasyMock.expect(describeResult.all()).andReturn(future).once()
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
+    when(describeResult.all()).thenReturn(future)
 
     val node = new Node(1, "localhost", 9092)
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
@@ -896,9 +892,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         describeResult
       }
     }
-    EasyMock.replay(describeResult)
     ConfigCommand.describeConfig(mockAdminClient, describeOpts)
-    EasyMock.reset(describeResult)
+    verify(describeResult).all()
   }
 
   @Test
@@ -909,10 +904,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       "--alter",
       "--add-config", "leader.replication.throttled.rate=10,follower.replication.throttled.rate=20"))
 
-    val mockZkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
-    val mockBroker: Broker = EasyMock.createNiceMock(classOf[Broker])
-    EasyMock.expect(mockZkClient.getBroker(1)).andReturn(Option(mockBroker))
-    EasyMock.replay(mockZkClient)
+    val mockZkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
+    val mockBroker: Broker = mock(classOf[Broker])
+    when(mockZkClient.getBroker(1)).thenReturn(Option(mockBroker))
 
     assertThrows(classOf[IllegalArgumentException],
       () => ConfigCommand.alterConfigWithZk(mockZkClient, alterOpts, new DummyAdminZkClient(zkClient)))
@@ -925,10 +919,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       "--entity-type", "brokers",
       "--describe"))
 
-    val mockZkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
-    val mockBroker: Broker = EasyMock.createNiceMock(classOf[Broker])
-    EasyMock.expect(mockZkClient.getBroker(1)).andReturn(Option(mockBroker))
-    EasyMock.replay(mockZkClient)
+    val mockZkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
+    val mockBroker: Broker = mock(classOf[Broker])
+    when(mockZkClient.getBroker(1)).thenReturn(Option(mockBroker))
 
     assertThrows(classOf[IllegalArgumentException],
       () => ConfigCommand.describeConfigWithZk(mockZkClient, describeOpts, new DummyAdminZkClient(zkClient)))
@@ -950,9 +943,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       }
     }
 
-    val mockZkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
-    EasyMock.expect(mockZkClient.getBroker(1)).andReturn(None)
-    EasyMock.replay(mockZkClient)
+    val mockZkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
+    when(mockZkClient.getBroker(1)).thenReturn(None)
 
     ConfigCommand.describeConfigWithZk(mockZkClient, describeOpts, new TestAdminZkClient(zkClient))
   }
@@ -1077,13 +1069,13 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val configEntries = util.Collections.singletonList(new ConfigEntry("num.io.threads", "5"))
     val future = new KafkaFutureImpl[util.Map[ConfigResource, Config]]
     future.complete(util.Collections.singletonMap(resource, new Config(configEntries)))
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
-    EasyMock.expect(describeResult.all()).andReturn(future).once()
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
+    when(describeResult.all()).thenReturn(future)
 
     val alterFuture = new KafkaFutureImpl[Void]
     alterFuture.complete(null)
-    val alterResult: AlterConfigsResult = EasyMock.createNiceMock(classOf[AlterConfigsResult])
-    EasyMock.expect(alterResult.all()).andReturn(alterFuture)
+    val alterResult: AlterConfigsResult = mock(classOf[AlterConfigsResult])
+    when(alterResult.all()).thenReturn(alterFuture)
 
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
       override def describeConfigs(resources: util.Collection[ConfigResource], options: DescribeConfigsOptions): DescribeConfigsResult = {
@@ -1105,11 +1097,10 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         alterResult
       }
     }
-    EasyMock.replay(alterResult, describeResult)
     ConfigCommand.alterConfig(mockAdminClient, alterOpts)
     assertEquals(Map("message.max.bytes" -> "10", "num.io.threads" -> "5", "leader.replication.throttled.rate" -> "10"),
       brokerConfigs.toMap)
-    EasyMock.reset(alterResult, describeResult)
+    verify(describeResult).all()
   }
 
   @Test
@@ -1125,9 +1116,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val emptyConfig = new Config(util.Collections.emptyList[ConfigEntry])
     val resultMap = Map(resourceCustom -> emptyConfig, resourceDefault -> emptyConfig).asJava
     future.complete(resultMap)
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
     // make sure it will be called 2 times: (1) for broker "1" (2) for default broker ""
-    EasyMock.expect(describeResult.all()).andReturn(future).times(2)
+    when(describeResult.all()).thenReturn(future)
 
     val node = new Node(1, "localhost", 9092)
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
@@ -1140,10 +1131,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         describeResult
       }
     }
-    EasyMock.replay(describeResult)
     ConfigCommand.describeConfig(mockAdminClient, describeOpts)
-    EasyMock.verify(describeResult)
-    EasyMock.reset(describeResult)
+    verify(describeResult, times(2)).all()
   }
 
   private def verifyAlterBrokerLoggerConfig(node: Node, resourceName: String, entityName: String,
@@ -1160,13 +1149,13 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val resource = new ConfigResource(ConfigResource.Type.BROKER_LOGGER, resourceName)
     val future = new KafkaFutureImpl[util.Map[ConfigResource, Config]]
     future.complete(util.Collections.singletonMap(resource, new Config(describeConfigEntries.asJava)))
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
-    EasyMock.expect(describeResult.all()).andReturn(future).once()
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
+    when(describeResult.all()).thenReturn(future)
 
     val alterFuture = new KafkaFutureImpl[Void]
     alterFuture.complete(null)
-    val alterResult: AlterConfigsResult = EasyMock.createNiceMock(classOf[AlterConfigsResult])
-    EasyMock.expect(alterResult.all()).andReturn(alterFuture)
+    val alterResult: AlterConfigsResult = mock(classOf[AlterConfigsResult])
+    when(alterResult.all()).thenReturn(alterFuture)
 
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
       override def describeConfigs(resources: util.Collection[ConfigResource], options: DescribeConfigsOptions): DescribeConfigsResult = {
@@ -1195,10 +1184,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
         alterResult
       }
     }
-    EasyMock.replay(alterResult, describeResult)
     ConfigCommand.alterConfig(mockAdminClient, alterOpts)
     assertTrue(alteredConfigs)
-    EasyMock.reset(alterResult, describeResult)
+    verify(describeResult).all()
   }
 
   @Test
@@ -1377,7 +1365,7 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
   }
 
   @Test
-  def shouldNotUpdateConfigIfNonExistingConfigIsDeletedUsingZookeper(): Unit = {
+  def shouldNotUpdateConfigIfNonExistingConfigIsDeletedUsingZookeeper(): Unit = {
     val createOpts = new ConfigCommandOptions(Array("--zookeeper", zkConnect,
       "--entity-name", "my-topic",
       "--entity-type", "topics",
@@ -1399,8 +1387,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
     val configEntries = List.empty[ConfigEntry].asJava
     val future = new KafkaFutureImpl[util.Map[ConfigResource, Config]]
     future.complete(util.Collections.singletonMap(resource, new Config(configEntries)))
-    val describeResult: DescribeConfigsResult = EasyMock.createNiceMock(classOf[DescribeConfigsResult])
-    EasyMock.expect(describeResult.all()).andReturn(future).once()
+    val describeResult: DescribeConfigsResult = mock(classOf[DescribeConfigsResult])
+    when(describeResult.all()).thenReturn(future)
 
     val node = new Node(1, "localhost", 9092)
     val mockAdminClient = new MockAdminClient(util.Collections.singletonList(node), node) {
@@ -1413,9 +1401,8 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       }
     }
 
-    EasyMock.replay(describeResult)
     assertThrows(classOf[InvalidConfigurationException], () => ConfigCommand.alterConfig(mockAdminClient, createOpts))
-    EasyMock.reset(describeResult)
+    verify(describeResult).all()
   }
 
   @Test
@@ -1441,10 +1428,9 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
       }
     }
 
-    val mockZkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
-    val mockBroker: Broker = EasyMock.createNiceMock(classOf[Broker])
-    EasyMock.expect(mockZkClient.getBroker(1)).andReturn(Option(mockBroker))
-    EasyMock.replay(mockZkClient)
+    val mockZkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
+    val mockBroker: Broker = mock(classOf[Broker])
+    when(mockZkClient.getBroker(1)).thenReturn(Option(mockBroker))
 
     assertThrows(classOf[IllegalArgumentException], () => ConfigCommand.alterConfigWithZk(mockZkClient, createOpts, new TestAdminZkClient(zkClient)))
   }
@@ -1635,17 +1621,15 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
 
   @Test
   def testQuotaDescribeEntities(): Unit = {
-    val zkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
+    val zkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
 
     def checkEntities(opts: Array[String], expectedFetches: Map[String, Seq[String]], expectedEntityNames: Seq[String]): Unit = {
       val entity = ConfigCommand.parseEntity(new ConfigCommandOptions(opts :+ "--describe"))
       expectedFetches.foreach {
-        case (name, values) => EasyMock.expect(zkClient.getAllEntitiesWithConfig(name)).andReturn(values)
+        case (name, values) => when(zkClient.getAllEntitiesWithConfig(name)).thenReturn(values)
       }
-      EasyMock.replay(zkClient)
       val entities = entity.getAllEntities(zkClient)
       assertEquals(expectedEntityNames, entities.map(e => e.fullSanitizedName))
-      EasyMock.reset(zkClient)
     }
 
     val clientId = "a-client"
@@ -1713,15 +1697,15 @@ class ConfigCommandTest extends QuorumTestHarness with Logging {
 
   class DummyAdminClient(node: Node) extends MockAdminClient(util.Collections.singletonList(node), node) {
     override def describeConfigs(resources: util.Collection[ConfigResource], options: DescribeConfigsOptions): DescribeConfigsResult =
-      EasyMock.createNiceMock(classOf[DescribeConfigsResult])
+      mock(classOf[DescribeConfigsResult])
     override def incrementalAlterConfigs(configs: util.Map[ConfigResource, util.Collection[AlterConfigOp]],
-      options: AlterConfigsOptions): AlterConfigsResult = EasyMock.createNiceMock(classOf[AlterConfigsResult])
+      options: AlterConfigsOptions): AlterConfigsResult = mock(classOf[AlterConfigsResult])
     override def alterConfigs(configs: util.Map[ConfigResource, Config], options: AlterConfigsOptions): AlterConfigsResult =
-      EasyMock.createNiceMock(classOf[AlterConfigsResult])
+      mock(classOf[AlterConfigsResult])
     override def describeClientQuotas(filter: ClientQuotaFilter, options: DescribeClientQuotasOptions): DescribeClientQuotasResult =
-      EasyMock.createNiceMock(classOf[DescribeClientQuotasResult])
+      mock(classOf[DescribeClientQuotasResult])
     override def alterClientQuotas(entries: util.Collection[ClientQuotaAlteration],
       options: AlterClientQuotasOptions): AlterClientQuotasResult =
-      EasyMock.createNiceMock(classOf[AlterClientQuotasResult])
+      mock(classOf[AlterClientQuotasResult])
   }
 }
diff --git a/core/src/test/scala/unit/kafka/controller/PartitionStateMachineTest.scala b/core/src/test/scala/unit/kafka/controller/PartitionStateMachineTest.scala
index 2119506..174b9f1 100644
--- a/core/src/test/scala/unit/kafka/controller/PartitionStateMachineTest.scala
+++ b/core/src/test/scala/unit/kafka/controller/PartitionStateMachineTest.scala
@@ -26,10 +26,10 @@ import kafka.zookeeper._
 import org.apache.kafka.common.TopicPartition
 import org.apache.zookeeper.KeeperException.Code
 import org.apache.zookeeper.data.Stat
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{BeforeEach, Test}
-import org.mockito.Mockito
+import org.mockito.ArgumentMatchers.{any, anyInt}
+import org.mockito.Mockito.{mock, verify, when}
 
 class PartitionStateMachineTest {
   private var controllerContext: ControllerContext = null
@@ -47,8 +47,8 @@ class PartitionStateMachineTest {
   def setUp(): Unit = {
     controllerContext = new ControllerContext
     controllerContext.epoch = controllerEpoch
-    mockZkClient = EasyMock.createMock(classOf[KafkaZkClient])
-    mockControllerBrokerRequestBatch = EasyMock.createMock(classOf[ControllerBrokerRequestBatch])
+    mockZkClient = mock(classOf[KafkaZkClient])
+    mockControllerBrokerRequestBatch = mock(classOf[ControllerBrokerRequestBatch])
     partitionStateMachine = new ZkPartitionStateMachine(config, new StateChangeLogger(brokerId, true, None), controllerContext,
       mockZkClient, mockControllerBrokerRequestBatch)
   }
@@ -85,19 +85,18 @@ class PartitionStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     controllerContext.putPartitionState(partition, NewPartition)
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
-      .andReturn(Seq(CreateResponse(Code.OK, null, Some(partition), null, ResponseMetadata(0, 0))))
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = true))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
+      .thenReturn(Seq(CreateResponse(Code.OK, null, Some(partition), null, ResponseMetadata(0, 0))))
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = true)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).createTopicPartitionStatesRaw(any(), anyInt())
     assertEquals(OnlinePartition, partitionState(partition))
   }
 
@@ -107,17 +106,16 @@ class PartitionStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     controllerContext.putPartitionState(partition, NewPartition)
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
-      .andThrow(new ZooKeeperClientException("test"))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
+      .thenThrow(new ZooKeeperClientException("test"))
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).createTopicPartitionStatesRaw(any(), anyInt())
     assertEquals(NewPartition, partitionState(partition))
   }
 
@@ -127,17 +125,16 @@ class PartitionStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     controllerContext.putPartitionState(partition, NewPartition)
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
-      .andReturn(Seq(CreateResponse(Code.NODEEXISTS, null, Some(partition), null, ResponseMetadata(0, 0))))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.createTopicPartitionStatesRaw(Map(partition -> leaderIsrAndControllerEpoch), controllerContext.epochZkVersion))
+      .thenReturn(Seq(CreateResponse(Code.NODEEXISTS, null, Some(partition), null, ResponseMetadata(0, 0))))
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).createTopicPartitionStatesRaw(any(), anyInt())
     assertEquals(NewPartition, partitionState(partition))
   }
 
@@ -165,22 +162,22 @@ class PartitionStateMachineTest {
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
     val stat = new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+      .thenReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))))
 
     val leaderAndIsrAfterElection = leaderAndIsr.newLeader(brokerId)
     val updatedLeaderAndIsr = leaderAndIsrAfterElection.withZkVersion(2)
-    EasyMock.expect(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
-      .andReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch), replicaAssignment(Seq(brokerId)), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
+      .thenReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
 
     partitionStateMachine.handleStateChanges(partitions, OnlinePartition, Option(PreferredReplicaPartitionLeaderElectionStrategy))
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch), replicaAssignment(Seq(brokerId)), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
+    verify(mockZkClient).updateLeaderAndIsr(any(), anyInt(), anyInt())
     assertEquals(OnlinePartition, partitionState(partition))
   }
 
@@ -201,25 +198,23 @@ class PartitionStateMachineTest {
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
     val stat = new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+      .thenReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))))
-
     val leaderAndIsrAfterElection = leaderAndIsr.newLeaderAndIsr(otherBrokerId, List(otherBrokerId))
     val updatedLeaderAndIsr = leaderAndIsrAfterElection.withZkVersion(2)
-    EasyMock.expect(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
-      .andReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
+    when(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
+      .thenReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
 
+    partitionStateMachine.handleStateChanges(partitions, OnlinePartition, Option(ControlledShutdownPartitionLeaderElectionStrategy))
+    verify(mockControllerBrokerRequestBatch).newBatch()
     // The leaderAndIsr request should be sent to both brokers, including the shutting down one
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId, otherBrokerId),
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId, otherBrokerId),
       partition, LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch), replicaAssignment(Seq(brokerId, otherBrokerId)),
-      isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
-
-    partitionStateMachine.handleStateChanges(partitions, OnlinePartition, Option(ControlledShutdownPartitionLeaderElectionStrategy))
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+      isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
+    verify(mockZkClient).updateLeaderAndIsr(any(), anyInt(), anyInt())
     assertEquals(OnlinePartition, partitionState(partition))
   }
 
@@ -254,28 +249,29 @@ class PartitionStateMachineTest {
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
     val stat = new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+      .thenReturn(Seq(GetDataResponse(Code.OK, null, Some(partition),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))))
 
-    EasyMock.expect(mockZkClient.getLogConfigs(Set.empty, config.originals()))
-      .andReturn((Map(partition.topic -> LogConfig()), Map.empty))
+    when(mockZkClient.getLogConfigs(Set.empty, config.originals()))
+      .thenReturn((Map(partition.topic -> LogConfig()), Map.empty[String, Exception]))
     val leaderAndIsrAfterElection = leaderAndIsr.newLeader(brokerId)
     val updatedLeaderAndIsr = leaderAndIsrAfterElection.withZkVersion(2)
-    EasyMock.expect(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
-      .andReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch), replicaAssignment(Seq(brokerId)), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
+      .thenReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
 
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch), replicaAssignment(Seq(brokerId)), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
+    verify(mockZkClient).getLogConfigs(any(), any())
+    verify(mockZkClient).updateLeaderAndIsr(any(), anyInt(), anyInt())
     assertEquals(OnlinePartition, partitionState(partition))
   }
 
@@ -300,52 +296,39 @@ class PartitionStateMachineTest {
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(leaderAndIsr, controllerEpoch)
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock
-      .expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andReturn(
-        Seq(
-          GetDataResponse(
-            Code.OK,
-            null,
-            Option(partition),
-            TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch),
-            new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
-            ResponseMetadata(0, 0)
-          )
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions)).thenReturn(
+      Seq(
+        GetDataResponse(
+          Code.OK,
+          null,
+          Option(partition),
+          TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch),
+          new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+          ResponseMetadata(0, 0)
         )
       )
+    )
 
     val leaderAndIsrAfterElection = leaderAndIsr.newLeaderAndIsr(brokerId, List(brokerId))
     val updatedLeaderAndIsr = leaderAndIsrAfterElection.withZkVersion(2)
-
-    EasyMock
-      .expect(
-        mockZkClient.updateLeaderAndIsr(
-          Map(partition -> leaderAndIsrAfterElection),
-          controllerEpoch,
-          controllerContext.epochZkVersion
-        )
-      )
-      .andReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
-    EasyMock.expect(
-      mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(
-        Seq(brokerId),
-        partition,
-        LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch),
-        replicaAssignment(Seq(leaderBrokerId, brokerId)),
-        false
-      )
-    )
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.updateLeaderAndIsr(Map(partition -> leaderAndIsrAfterElection), controllerEpoch, controllerContext.epochZkVersion))
+      .thenReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
 
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(true))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(
+      Seq(brokerId),
+      partition,
+      LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch),
+      replicaAssignment(Seq(leaderBrokerId, brokerId)),
+      false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
+    verify(mockZkClient).updateLeaderAndIsr(any(), anyInt(), anyInt())
     assertEquals(OnlinePartition, partitionState(partition))
   }
 
@@ -358,19 +341,17 @@ class PartitionStateMachineTest {
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(leaderAndIsr, controllerEpoch)
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andThrow(new ZooKeeperClientException(""))
-
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+      .thenThrow(new ZooKeeperClientException(""))
 
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
     assertEquals(OfflinePartition, partitionState(partition))
   }
 
@@ -384,20 +365,18 @@ class PartitionStateMachineTest {
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
     val stat = new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-      .andReturn(Seq(GetDataResponse(Code.NONODE, null, Some(partition),
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+      .thenReturn(Seq(GetDataResponse(Code.NONODE, null, Some(partition),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))))
 
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
-
     partitionStateMachine.handleStateChanges(
       partitions,
       OnlinePartition,
       Option(OfflinePartitionLeaderElectionStrategy(false))
     )
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
     assertEquals(OfflinePartition, partitionState(partition))
   }
 
@@ -422,14 +401,12 @@ class PartitionStateMachineTest {
       val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(leaderAndIsr, controllerEpoch)
       val getDataResponses = partitions.map {p => GetDataResponse(Code.OK, null, Some(p),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))}
-      EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions))
-        .andReturn(getDataResponses)
+      when(mockZkClient.getTopicPartitionStatesRaw(partitions))
+        .thenReturn(getDataResponses)
     }
     prepareMockToGetTopicPartitionsStatesRaw()
-
     def prepareMockToGetLogConfigs(): Unit = {
-      EasyMock.expect(mockZkClient.getLogConfigs(Set.empty, config.originals()))
-        .andReturn(Map.empty, Map.empty)
+      when(mockZkClient.getLogConfigs(Set.empty, config.originals())).thenReturn((Map.empty[String, LogConfig], Map.empty[String, Exception]))
     }
     prepareMockToGetLogConfigs()
 
@@ -437,8 +414,8 @@ class PartitionStateMachineTest {
       val updatedLeaderAndIsr: Map[TopicPartition, LeaderAndIsr] = partitions.map { partition =>
         partition -> leaderAndIsr.newLeaderAndIsr(brokerId, List(brokerId))
       }.toMap
-      EasyMock.expect(mockZkClient.updateLeaderAndIsr(updatedLeaderAndIsr, controllerEpoch, controllerContext.epochZkVersion))
-        .andReturn(UpdateLeaderAndIsrResult(updatedLeaderAndIsr.map { case (k, v) => k -> Right(v) }, Seq.empty))
+      when(mockZkClient.updateLeaderAndIsr(updatedLeaderAndIsr, controllerEpoch, controllerContext.epochZkVersion))
+        .thenReturn(UpdateLeaderAndIsrResult(updatedLeaderAndIsr.map { case (k, v) => k -> Right(v) }, Seq.empty))
     }
     prepareMockToUpdateLeaderAndIsr()
   }
@@ -460,7 +437,6 @@ class PartitionStateMachineTest {
     }
 
     prepareMockToElectLeaderForPartitions(partitions)
-    EasyMock.replay(mockZkClient)
 
     partitionStateMachine.handleStateChanges(partitions, NewPartition)
     partitionStateMachine.handleStateChanges(partitions, OfflinePartition)
@@ -477,7 +453,7 @@ class PartitionStateMachineTest {
     * to the offlinePartitionCount
     */
   @Test
-  def testNoOfflinePartitionsChangeForTopicsBeingDeleted() = {
+  def testNoOfflinePartitionsChangeForTopicsBeingDeleted(): Unit = {
     val partitionIds = Seq(0, 1, 2, 3)
     val topic = "test"
     val partitions = partitionIds.map(new TopicPartition(topic, _))
@@ -500,7 +476,7 @@ class PartitionStateMachineTest {
     * should be decremented to 0.
     */
   @Test
-  def testUpdatingOfflinePartitionsCountDuringTopicDeletion() = {
+  def testUpdatingOfflinePartitionsCountDuringTopicDeletion(): Unit = {
     val partitionIds = Seq(0, 1, 2, 3)
     val topic = "test"
     val partitions = partitionIds.map(new TopicPartition("test", _))
@@ -510,7 +486,7 @@ class PartitionStateMachineTest {
 
     val partitionStateMachine = new MockPartitionStateMachine(controllerContext, uncleanLeaderElectionEnabled = false)
     val replicaStateMachine = new MockReplicaStateMachine(controllerContext)
-    val deletionClient = Mockito.mock(classOf[DeletionClient])
+    val deletionClient = mock(classOf[DeletionClient])
     val topicDeletionManager = new TopicDeletionManager(config, controllerContext,
       replicaStateMachine, partitionStateMachine, deletionClient)
 
diff --git a/core/src/test/scala/unit/kafka/controller/ReplicaStateMachineTest.scala b/core/src/test/scala/unit/kafka/controller/ReplicaStateMachineTest.scala
index de43e05..ecb25ea 100644
--- a/core/src/test/scala/unit/kafka/controller/ReplicaStateMachineTest.scala
+++ b/core/src/test/scala/unit/kafka/controller/ReplicaStateMachineTest.scala
@@ -28,9 +28,11 @@ import org.apache.kafka.common.network.ListenerName
 import org.apache.kafka.common.security.auth.SecurityProtocol
 import org.apache.zookeeper.KeeperException.Code
 import org.apache.zookeeper.data.Stat
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{BeforeEach, Test}
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.{any, anyInt}
+import org.mockito.Mockito.{mock, verify, when}
 
 class ReplicaStateMachineTest {
   private var controllerContext: ControllerContext = null
@@ -50,8 +52,8 @@ class ReplicaStateMachineTest {
   def setUp(): Unit = {
     controllerContext = new ControllerContext
     controllerContext.epoch = controllerEpoch
-    mockZkClient = EasyMock.createMock(classOf[KafkaZkClient])
-    mockControllerBrokerRequestBatch = EasyMock.createMock(classOf[ControllerBrokerRequestBatch])
+    mockZkClient = mock(classOf[KafkaZkClient])
+    mockControllerBrokerRequestBatch = mock(classOf[ControllerBrokerRequestBatch])
     replicaStateMachine = new ZkReplicaStateMachine(config, new StateChangeLogger(brokerId, true, None),
       controllerContext, mockZkClient, mockControllerBrokerRequestBatch)
   }
@@ -150,14 +152,12 @@ class ReplicaStateMachineTest {
     val liveBrokerEpochs = Map(Broker(brokerId, Seq(endpoint1), rack = None) -> 1L)
     controllerContext.setLiveBrokers(liveBrokerEpochs)
     controllerContext.putReplicaState(replica, NewReplica)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addStopReplicaRequestForBrokers(EasyMock.eq(Seq(brokerId)), EasyMock.eq(partition), EasyMock.eq(false)))
-    EasyMock.expect(mockControllerBrokerRequestBatch.addUpdateMetadataRequestForBrokers(EasyMock.eq(Seq(brokerId)),  EasyMock.eq(Set(partition))))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
 
-    EasyMock.replay(mockControllerBrokerRequestBatch)
     replicaStateMachine.handleStateChanges(replicas, OfflineReplica)
-    EasyMock.verify(mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addStopReplicaRequestForBrokers(ArgumentMatchers.eq(Seq(brokerId)), ArgumentMatchers.eq(partition), ArgumentMatchers.eq(false))
+    verify(mockControllerBrokerRequestBatch).addUpdateMetadataRequestForBrokers(ArgumentMatchers.eq(Seq(brokerId)),  ArgumentMatchers.eq(Set(partition)))
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(ArgumentMatchers.eq(controllerEpoch))
     assertEquals(OfflineReplica, replicaState(replica))
   }
 
@@ -192,13 +192,12 @@ class ReplicaStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
+
     replicaStateMachine.handleStateChanges(replicas, OnlineReplica)
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
     assertEquals(OnlineReplica, replicaState(replica))
   }
 
@@ -213,23 +212,23 @@ class ReplicaStateMachineTest {
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
 
     val stat = new Stat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addStopReplicaRequestForBrokers(EasyMock.eq(Seq(brokerId)), EasyMock.eq(partition), EasyMock.eq(false)))
     val adjustedLeaderAndIsr = leaderAndIsr.newLeaderAndIsr(LeaderAndIsr.NoLeader, List(otherBrokerId))
     val updatedLeaderAndIsr = adjustedLeaderAndIsr.withZkVersion(adjustedLeaderAndIsr .zkVersion + 1)
     val updatedLeaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(updatedLeaderAndIsr, controllerEpoch)
-    EasyMock.expect(mockZkClient.getTopicPartitionStatesRaw(partitions)).andReturn(
+    when(mockZkClient.getTopicPartitionStatesRaw(partitions)).thenReturn(
       Seq(GetDataResponse(Code.OK, null, Some(partition),
         TopicPartitionStateZNode.encode(leaderIsrAndControllerEpoch), stat, ResponseMetadata(0, 0))))
-    EasyMock.expect(mockZkClient.updateLeaderAndIsr(Map(partition -> adjustedLeaderAndIsr), controllerEpoch, controllerContext.epochZkVersion))
-      .andReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(otherBrokerId),
-      partition, updatedLeaderIsrAndControllerEpoch, replicaAssignment(replicaIds), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
+    when(mockZkClient.updateLeaderAndIsr(Map(partition -> adjustedLeaderAndIsr), controllerEpoch, controllerContext.epochZkVersion))
+      .thenReturn(UpdateLeaderAndIsrResult(Map(partition -> Right(updatedLeaderAndIsr)), Seq.empty))
 
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
     replicaStateMachine.handleStateChanges(replicas, OfflineReplica)
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addStopReplicaRequestForBrokers(ArgumentMatchers.eq(Seq(brokerId)), ArgumentMatchers.eq(partition), ArgumentMatchers.eq(false))
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(otherBrokerId),
+      partition, updatedLeaderIsrAndControllerEpoch, replicaAssignment(replicaIds), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
+    verify(mockZkClient).getTopicPartitionStatesRaw(any())
+    verify(mockZkClient).updateLeaderAndIsr(any(), anyInt(), anyInt())
     assertEquals(updatedLeaderIsrAndControllerEpoch, controllerContext.partitionLeadershipInfo(partition).get)
     assertEquals(OfflineReplica, replicaState(replica))
   }
@@ -265,25 +264,21 @@ class ReplicaStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
     replicaStateMachine.handleStateChanges(replicas, OnlineReplica)
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
     assertEquals(OnlineReplica, replicaState(replica))
   }
 
   @Test
   def testOfflineReplicaToReplicaDeletionStartedTransition(): Unit = {
     controllerContext.putReplicaState(replica, OfflineReplica)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addStopReplicaRequestForBrokers(Seq(brokerId), partition, true))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
     replicaStateMachine.handleStateChanges(replicas, ReplicaDeletionStarted)
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addStopReplicaRequestForBrokers(Seq(brokerId), partition, true)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
     assertEquals(ReplicaDeletionStarted, replicaState(replica))
   }
 
@@ -383,13 +378,11 @@ class ReplicaStateMachineTest {
     controllerContext.updatePartitionFullReplicaAssignment(partition, ReplicaAssignment(Seq(brokerId)))
     val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(LeaderAndIsr(brokerId, List(brokerId)), controllerEpoch)
     controllerContext.putPartitionLeadershipInfo(partition, leaderIsrAndControllerEpoch)
-    EasyMock.expect(mockControllerBrokerRequestBatch.newBatch())
-    EasyMock.expect(mockControllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(Seq(brokerId),
-      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false))
-    EasyMock.expect(mockControllerBrokerRequestBatch.sendRequestsToBrokers(controllerEpoch))
-    EasyMock.replay(mockZkClient, mockControllerBrokerRequestBatch)
     replicaStateMachine.handleStateChanges(replicas, OnlineReplica)
-    EasyMock.verify(mockZkClient, mockControllerBrokerRequestBatch)
+    verify(mockControllerBrokerRequestBatch).newBatch()
+    verify(mockControllerBrokerRequestBatch).addLeaderAndIsrRequestForBrokers(Seq(brokerId),
+      partition, leaderIsrAndControllerEpoch, replicaAssignment(Seq(brokerId)), isNew = false)
+    verify(mockControllerBrokerRequestBatch).sendRequestsToBrokers(controllerEpoch)
     assertEquals(OnlineReplica, replicaState(replica))
   }
 
diff --git a/core/src/test/scala/unit/kafka/coordinator/AbstractCoordinatorConcurrencyTest.scala b/core/src/test/scala/unit/kafka/coordinator/AbstractCoordinatorConcurrencyTest.scala
index ddd3c18..d741508 100644
--- a/core/src/test/scala/unit/kafka/coordinator/AbstractCoordinatorConcurrencyTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/AbstractCoordinatorConcurrencyTest.scala
@@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.locks.Lock
 
 import kafka.coordinator.AbstractCoordinatorConcurrencyTest._
-import kafka.log.{AppendOrigin, UnifiedLog, LogConfig}
+import kafka.log.{AppendOrigin, LogConfig, UnifiedLog}
 import kafka.server._
 import kafka.utils._
 import kafka.utils.timer.MockTimer
@@ -32,8 +32,8 @@ import org.apache.kafka.common.TopicPartition
 import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.record.{MemoryRecords, RecordBatch, RecordConversionStats}
 import org.apache.kafka.common.requests.ProduceResponse.PartitionResponse
-import org.easymock.EasyMock
 import org.junit.jupiter.api.{AfterEach, BeforeEach}
+import org.mockito.Mockito.{mock, withSettings, CALLS_REAL_METHODS}
 
 import scala.collection._
 import scala.jdk.CollectionConverters._
@@ -54,15 +54,14 @@ abstract class AbstractCoordinatorConcurrencyTest[M <: CoordinatorMember] {
   @BeforeEach
   def setUp(): Unit = {
 
-    replicaManager = EasyMock.partialMockBuilder(classOf[TestReplicaManager]).createMock()
+    replicaManager = mock(classOf[TestReplicaManager], withSettings().defaultAnswer(CALLS_REAL_METHODS))
     replicaManager.createDelayedProducePurgatory(timer)
 
-    zkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
+    zkClient = mock(classOf[KafkaZkClient])
   }
 
   @AfterEach
   def tearDown(): Unit = {
-    EasyMock.reset(replicaManager)
     if (executor != null)
       executor.shutdownNow()
   }
@@ -79,7 +78,6 @@ abstract class AbstractCoordinatorConcurrencyTest[M <: CoordinatorMember] {
     * in a bad state. Operations in the normal sequence should continue to work as expected.
     */
   def verifyConcurrentRandomSequences(createMembers: String => Set[M], operations: Seq[Operation]): Unit = {
-    EasyMock.reset(replicaManager)
     for (i <- 0 to 10) {
       // Run some random operations
       RandomOperationSequence(createMembers(s"random$i"), operations).run()
diff --git a/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorConcurrencyTest.scala b/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorConcurrencyTest.scala
index 2ef487c..9d8f342 100644
--- a/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorConcurrencyTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorConcurrencyTest.scala
@@ -33,9 +33,9 @@ import org.apache.kafka.common.message.LeaveGroupRequestData.MemberIdentity
 import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.requests.{JoinGroupRequest, OffsetFetchResponse}
 import org.apache.kafka.common.utils.Time
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
+import org.mockito.Mockito.when
 
 import scala.collection._
 import scala.concurrent.duration.Duration
@@ -69,10 +69,8 @@ class GroupCoordinatorConcurrencyTest extends AbstractCoordinatorConcurrencyTest
   override def setUp(): Unit = {
     super.setUp()
 
-    EasyMock.expect(zkClient.getTopicPartitionCount(Topic.GROUP_METADATA_TOPIC_NAME))
-      .andReturn(Some(numPartitions))
-      .anyTimes()
-    EasyMock.replay(zkClient)
+    when(zkClient.getTopicPartitionCount(Topic.GROUP_METADATA_TOPIC_NAME))
+      .thenReturn(Some(numPartitions))
 
     serverProps.setProperty(KafkaConfig.GroupMinSessionTimeoutMsProp, ConsumerMinSessionTimeout.toString)
     serverProps.setProperty(KafkaConfig.GroupMaxSessionTimeoutMsProp, ConsumerMaxSessionTimeout.toString)
diff --git a/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorTest.scala b/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorTest.scala
index 0784259..8e88c84 100644
--- a/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/group/GroupCoordinatorTest.scala
@@ -27,7 +27,6 @@ import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.record.{MemoryRecords, RecordBatch}
 import org.apache.kafka.common.requests.ProduceResponse.PartitionResponse
 import org.apache.kafka.common.requests.{JoinGroupRequest, OffsetCommitRequest, OffsetFetchResponse, TransactionResult}
-import org.easymock.{Capture, EasyMock, IAnswer}
 
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.locks.ReentrantLock
@@ -41,6 +40,9 @@ import org.apache.kafka.common.metrics.Metrics
 import org.apache.kafka.common.message.LeaveGroupRequestData.MemberIdentity
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.ArgumentMatchers.{any, anyLong, anyShort}
+import org.mockito.Mockito.{mock, when}
 
 import scala.jdk.CollectionConverters._
 import scala.collection.{Seq, mutable}
@@ -101,12 +103,11 @@ class GroupCoordinatorTest {
     val ret = mutable.Map[String, Map[Int, Seq[Int]]]()
     ret += (Topic.GROUP_METADATA_TOPIC_NAME -> Map(0 -> Seq(1), 1 -> Seq(1)))
 
-    replicaManager = EasyMock.createNiceMock(classOf[ReplicaManager])
+    replicaManager = mock(classOf[ReplicaManager])
 
-    zkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
+    zkClient = mock(classOf[KafkaZkClient])
     // make two partitions of the group topic to make sure some partitions are not owned by the coordinator
-    EasyMock.expect(zkClient.getTopicPartitionCount(Topic.GROUP_METADATA_TOPIC_NAME)).andReturn(Some(2))
-    EasyMock.replay(zkClient)
+    when(zkClient.getTopicPartitionCount(Topic.GROUP_METADATA_TOPIC_NAME)).thenReturn(Some(2))
 
     timer = new MockTimer
 
@@ -126,7 +127,6 @@ class GroupCoordinatorTest {
 
   @AfterEach
   def tearDown(): Unit = {
-    EasyMock.reset(replicaManager)
     if (groupCoordinator != null)
       groupCoordinator.shutdown()
   }
@@ -185,9 +185,7 @@ class GroupCoordinatorTest {
 
     // After loading, we should be able to access the group
     val otherGroupMetadataTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, otherGroupPartitionId)
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getLog(otherGroupMetadataTopicPartition)).andReturn(None)
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getLog(otherGroupMetadataTopicPartition)).thenReturn(None)
     // Call removeGroupsAndOffsets so that partition removed from loadingPartitions
     groupCoordinator.groupManager.removeGroupsAndOffsets(otherGroupMetadataTopicPartition, Some(1), group => {})
     groupCoordinator.groupManager.loadGroupsAndOffsets(otherGroupMetadataTopicPartition, 1, group => {}, 0L)
@@ -210,7 +208,6 @@ class GroupCoordinatorTest {
     var joinGroupResult = dynamicJoinGroup(otherGroupId, memberId, protocolType, protocols)
     assertEquals(Errors.NOT_COORDINATOR, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     joinGroupResult = staticJoinGroup(otherGroupId, memberId, groupInstanceId, protocolType, protocols)
     assertEquals(Errors.NOT_COORDINATOR, joinGroupResult.error)
   }
@@ -224,7 +221,6 @@ class GroupCoordinatorTest {
       futures += sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
       if (i != 1)
         timer.advanceClock(GroupInitialRebalanceDelay)
-      EasyMock.reset(replicaManager)
     }
     // advance clock beyond rebalanceTimeout
     timer.advanceClock(GroupInitialRebalanceDelay + 1)
@@ -244,7 +240,6 @@ class GroupCoordinatorTest {
 
     // First JoinRequests
     var futures = 1.to(nbMembers).map { _ =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -254,7 +249,6 @@ class GroupCoordinatorTest {
 
     // Second JoinRequests
     futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -273,7 +267,6 @@ class GroupCoordinatorTest {
     // Members which were accepted can rejoin, others are rejected, while
     // completing rebalance
     futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -291,7 +284,6 @@ class GroupCoordinatorTest {
 
     // JoinRequests
     var futures = 1.to(nbMembers).map { _ =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -312,7 +304,6 @@ class GroupCoordinatorTest {
     // completing rebalance
     val memberIds = joinGroupResults.map(_.memberId)
     futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -330,7 +321,6 @@ class GroupCoordinatorTest {
 
     // JoinRequests
     var futures = instanceIds.map { instanceId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols,
         instanceId, DefaultSessionTimeout, DefaultRebalanceTimeout)
     }
@@ -351,7 +341,6 @@ class GroupCoordinatorTest {
     // completing rebalance
     val memberIds = joinGroupResults.map(_.memberId)
     futures = instanceIds.zip(memberIds).map { case (instanceId, memberId) =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         instanceId, DefaultSessionTimeout, DefaultRebalanceTimeout)
     }
@@ -369,7 +358,6 @@ class GroupCoordinatorTest {
 
     // First JoinRequests
     var futures = 1.to(nbMembers).map { _ =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -379,14 +367,12 @@ class GroupCoordinatorTest {
 
     // Second JoinRequests
     memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
 
     // Members can rejoin while rebalancing
     futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -418,7 +404,6 @@ class GroupCoordinatorTest {
     groupCoordinator.prepareRebalance(group, "")
 
     val futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, GroupMaxSessionTimeout, DefaultRebalanceTimeout)
     }
@@ -458,7 +443,6 @@ class GroupCoordinatorTest {
     var joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     joinGroupResult = staticJoinGroup(groupId, memberId, groupInstanceId, protocolType, protocols)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, joinGroupResult.error)
   }
@@ -488,7 +472,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinGroupResult = await(sendJoinGroup(groupId, otherMemberId, "connect", protocols), 1)
     assertEquals(Errors.INCONSISTENT_GROUP_PROTOCOL, otherJoinGroupResult.error)
   }
@@ -500,7 +483,6 @@ class GroupCoordinatorTest {
     var joinGroupResult = dynamicJoinGroup(groupId, memberId, "", protocols)
     assertEquals(Errors.INCONSISTENT_GROUP_PROTOCOL, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     joinGroupResult = staticJoinGroup(groupId, memberId, groupInstanceId, "", protocols)
     assertEquals(Errors.INCONSISTENT_GROUP_PROTOCOL, joinGroupResult.error)
   }
@@ -527,7 +509,6 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, joinResult.error)
     assertEquals(0, group.allMemberMetadata.count(_.isNew))
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinResult.generationId, memberId, Map(memberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
     assertEquals(1, group.size)
@@ -562,7 +543,6 @@ class GroupCoordinatorTest {
     val group = groupOpt.get
     assertEquals(0, group.allMemberMetadata.count(_.isNew))
 
-    EasyMock.reset(replicaManager)
     val responseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, None, sessionTimeout, rebalanceTimeout)
     assertFalse(responseFuture.isCompleted)
 
@@ -595,15 +575,12 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId,
       Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols,
       requireKnownMemberId = false)
 
@@ -628,15 +605,12 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId,
       Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols,
       requireKnownMemberId = false)
 
@@ -647,10 +621,8 @@ class GroupCoordinatorTest {
     val secondGenerationId = joinResult.generationId
     val secondMemberId = otherJoinResult.memberId
 
-    EasyMock.reset(replicaManager)
     sendSyncGroupFollower(groupId, secondGenerationId, secondMemberId)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, secondGenerationId, firstMemberId,
       Map(firstMemberId -> Array.emptyByteArray, secondMemberId -> Array.emptyByteArray))
     assertEquals(Errors.NONE, syncGroupResult.error)
@@ -659,10 +631,8 @@ class GroupCoordinatorTest {
   }
 
   private def verifySessionExpiration(groupId: String): Unit = {
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject()))
-      .andReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition]))
+      .thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
 
     timer.advanceClock(DefaultSessionTimeout + 1)
 
@@ -678,7 +648,6 @@ class GroupCoordinatorTest {
 
     val joinGroupFuture = sendJoinGroup(groupId, memberId, protocolType, List(("range", metadata)))
 
-    EasyMock.reset(replicaManager)
     val otherJoinGroupResult = dynamicJoinGroup(groupId, otherMemberId, protocolType, List(("roundrobin", metadata)))
     timer.advanceClock(GroupInitialRebalanceDelay + 1)
 
@@ -695,7 +664,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinGroupResult = await(sendJoinGroup(groupId, otherMemberId, protocolType, protocols), 1)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, otherJoinGroupResult.error)
   }
@@ -728,13 +696,11 @@ class GroupCoordinatorTest {
     val memberId = joinGroupResult.memberId
 
     // Sending an inconsistent protocol shall be refused
-    EasyMock.reset(replicaManager)
     responseFuture = sendJoinGroup(groupId, memberId, protocolType, List(), requireKnownMemberId = true)
     joinGroupResult = Await.result(responseFuture, Duration(DefaultRebalanceTimeout + 1, TimeUnit.MILLISECONDS))
     assertEquals(Errors.INCONSISTENT_GROUP_PROTOCOL, joinGroupResult.error)
 
     // Sending consistent protocol shall be accepted
-    EasyMock.reset(replicaManager)
     responseFuture = sendJoinGroup(groupId, memberId, protocolType, protocols, requireKnownMemberId = true)
     timer.advanceClock(GroupInitialRebalanceDelay + 1)
     joinGroupResult = Await.result(responseFuture, Duration(DefaultRebalanceTimeout + 1, TimeUnit.MILLISECONDS))
@@ -752,7 +718,6 @@ class GroupCoordinatorTest {
     var joinGroupResult = staticJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, groupInstanceId, protocolType, protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val unknownMemberId = "unknown_member"
     joinGroupResult = staticJoinGroup(groupId, unknownMemberId, groupInstanceId, protocolType, protocols)
     assertEquals(Errors.FENCED_INSTANCE_ID, joinGroupResult.error)
@@ -767,13 +732,11 @@ class GroupCoordinatorTest {
     timer.advanceClock(1)
     assertTrue(getGroup(groupId).is(PreparingRebalance))
 
-    EasyMock.reset(replicaManager)
     timer.advanceClock(1)
     // Old follower rejoins group will be matching current member.id.
     val oldFollowerJoinGroupFuture =
       sendJoinGroup(groupId, rebalanceResult.followerId, protocolType, protocols, groupInstanceId = Some(followerInstanceId))
 
-    EasyMock.reset(replicaManager)
     timer.advanceClock(1)
     // Duplicate follower joins group with unknown member id will trigger member.id replacement.
     val duplicateFollowerJoinFuture =
@@ -801,7 +764,6 @@ class GroupCoordinatorTest {
     timer.advanceClock(1)
     assertTrue(getGroup(groupId).is(PreparingRebalance))
 
-    EasyMock.reset(replicaManager)
     timer.advanceClock(1)
     // Old follower rejoins group will match current member.id.
     val oldFollowerJoinGroupFuture =
@@ -834,11 +796,9 @@ class GroupCoordinatorTest {
     // Duplicate follower joins group with unknown member id will trigger member.id replacement,
     // and will also trigger a rebalance under CompletingRebalance state; the old follower sync callback
     // will return fenced exception while broker replaces the member identity with the duplicate follower joins.
-    EasyMock.reset(replicaManager)
     val oldFollowerSyncGroupFuture = sendSyncGroupFollower(groupId, oldFollowerJoinGroupResult.generationId,
       oldFollowerJoinGroupResult.memberId, Some(protocolType), Some(protocolName), Some(followerInstanceId))
 
-    EasyMock.reset(replicaManager)
     val duplicateFollowerJoinFuture =
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, groupInstanceId = Some(followerInstanceId))
     timer.advanceClock(1)
@@ -871,12 +831,10 @@ class GroupCoordinatorTest {
     timer.advanceClock(1)
     assertTrue(getGroup(groupId).is(PreparingRebalance))
 
-    EasyMock.reset(replicaManager)
     // Duplicate follower joins group will trigger member.id replacement.
     val duplicateFollowerJoinGroupFuture =
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, groupInstanceId = Some(followerInstanceId))
 
-    EasyMock.reset(replicaManager)
     timer.advanceClock(1)
     // Old follower rejoins group will fail because member.id already updated.
     val oldFollowerJoinGroupFuture =
@@ -913,7 +871,6 @@ class GroupCoordinatorTest {
     var joinGroupResult = staticJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, groupInstanceId, protocolType, protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val assignedMemberId = joinGroupResult.memberId
     // The second join group should return immediately since we are using the same metadata during CompletingRebalance.
     val rejoinResponseFuture = sendJoinGroup(groupId, assignedMemberId, protocolType, protocols, Some(groupInstanceId))
@@ -922,7 +879,6 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, joinGroupResult.error)
     assertTrue(getGroup(groupId).is(CompletingRebalance))
 
-    EasyMock.reset(replicaManager)
     val syncGroupFuture = sendSyncGroupLeader(groupId, joinGroupResult.generationId, assignedMemberId,
       Some(protocolType), Some(protocolName), Some(groupInstanceId), Map(assignedMemberId -> Array[Byte]()))
     timer.advanceClock(1)
@@ -947,18 +903,15 @@ class GroupCoordinatorTest {
       Some(protocolType),
       rebalanceResult.leaderId)
 
-    EasyMock.reset(replicaManager)
     val oldLeaderJoinGroupResult = staticJoinGroup(groupId, rebalanceResult.leaderId, leaderInstanceId, protocolType, protocolSuperset, clockAdvance = 1)
     assertEquals(Errors.FENCED_INSTANCE_ID, oldLeaderJoinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     // Old leader will get fenced.
     val oldLeaderSyncGroupResult = syncGroupLeader(groupId, rebalanceResult.generation, rebalanceResult.leaderId,
       Map.empty, None, None, Some(leaderInstanceId))
     assertEquals(Errors.FENCED_INSTANCE_ID, oldLeaderSyncGroupResult.error)
 
     // Calling sync on old leader.id will fail because that leader.id is no longer valid and replaced.
-    EasyMock.reset(replicaManager)
     val newLeaderSyncGroupResult = syncGroupLeader(groupId, rebalanceResult.generation, joinGroupResult.leaderId, Map.empty)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, newLeaderSyncGroupResult.error)
   }
@@ -1067,13 +1020,11 @@ class GroupCoordinatorTest {
       Stable,
       Some(protocolType))
 
-    EasyMock.reset(replicaManager)
     // Join with old member id will not fail because the member id is not updated because of persistence failure
     assertNotEquals(rebalanceResult.followerId, joinGroupResult.memberId)
     val oldFollowerJoinGroupResult = staticJoinGroup(groupId, rebalanceResult.followerId, followerInstanceId, protocolType, newProtocols, clockAdvance = 1)
     assertEquals(Errors.NONE, oldFollowerJoinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     // Sync with old member id will also not fail because the member id is not updated because of persistence failure
     val syncGroupWithOldMemberIdResult = syncGroupFollower(groupId, rebalanceResult.generation,
       rebalanceResult.followerId, None, None, Some(followerInstanceId))
@@ -1099,19 +1050,16 @@ class GroupCoordinatorTest {
       Stable,
       Some(protocolType))
 
-    EasyMock.reset(replicaManager)
     // Join with old member id will fail because the member id is updated
     assertNotEquals(rebalanceResult.followerId, joinGroupResult.memberId)
     val oldFollowerJoinGroupResult = staticJoinGroup(groupId, rebalanceResult.followerId, followerInstanceId, protocolType, newProtocols, clockAdvance = 1)
     assertEquals(Errors.FENCED_INSTANCE_ID, oldFollowerJoinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     // Sync with old member id will fail because the member id is updated
     val syncGroupWithOldMemberIdResult = syncGroupFollower(groupId, rebalanceResult.generation,
       rebalanceResult.followerId, None, None, Some(followerInstanceId))
     assertEquals(Errors.FENCED_INSTANCE_ID, syncGroupWithOldMemberIdResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupWithNewMemberIdResult = syncGroupFollower(groupId, rebalanceResult.generation,
       joinGroupResult.memberId, None, None, Some(followerInstanceId))
     assertEquals(Errors.NONE, syncGroupWithNewMemberIdResult.error)
@@ -1126,7 +1074,6 @@ class GroupCoordinatorTest {
     val leaderRejoinGroupFuture = sendJoinGroup(groupId, rebalanceResult.leaderId, protocolType,
       protocolSuperset, Some(leaderInstanceId))
     // Rebalance complete immediately after follower rejoin.
-    EasyMock.reset(replicaManager)
     val followerRejoinWithFuture = sendJoinGroup(groupId, rebalanceResult.followerId, protocolType,
       protocolSuperset, Some(followerInstanceId))
 
@@ -1151,7 +1098,6 @@ class GroupCoordinatorTest {
       rebalanceResult.leaderId,
       rebalanceResult.followerId)
 
-    EasyMock.reset(replicaManager)
     // The follower protocol changed from protocolSuperset to general protocols.
     val followerRejoinWithProtocolChangeFuture = sendJoinGroup(groupId, rebalanceResult.followerId,
       protocolType, protocols, Some(followerInstanceId))
@@ -1187,7 +1133,6 @@ class GroupCoordinatorTest {
 
     assertNotEquals(rebalanceResult.followerId, joinGroupResult.memberId)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupFollower(groupId, rebalanceResult.generation, joinGroupResult.memberId)
     assertEquals(Errors.NONE, syncGroupResult.error)
     assertEquals(rebalanceResult.followerAssignment, syncGroupResult.memberAssignment)
@@ -1244,11 +1189,9 @@ class GroupCoordinatorTest {
     val syncGroupResult = syncGroupLeader(groupId, rebalanceResult.generation, rebalanceResult.leaderId, Map.empty)
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val validHeartbeatResult = heartbeat(groupId, rebalanceResult.leaderId, rebalanceResult.generation)
     assertEquals(Errors.NONE, validHeartbeatResult)
 
-    EasyMock.reset(replicaManager)
     val invalidHeartbeatResult = heartbeat(groupId, invalidMemberId, rebalanceResult.generation, Some(leaderInstanceId))
     assertEquals(Errors.FENCED_INSTANCE_ID, invalidHeartbeatResult)
   }
@@ -1260,7 +1203,6 @@ class GroupCoordinatorTest {
     val timeAdvance = 1
     var lastMemberId = initialResult.leaderId
     for (_ <- 1 to 5) {
-      EasyMock.reset(replicaManager)
       val joinGroupResult = staticJoinGroupWithPersistence(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID,
         leaderInstanceId, protocolType, protocols, clockAdvance = timeAdvance)
       assertTrue(joinGroupResult.memberId.startsWith(leaderInstanceId))
@@ -1291,11 +1233,9 @@ class GroupCoordinatorTest {
 
     val tp = new TopicPartition("topic", 0)
     val offset = offsetAndMetadata(0)
-    EasyMock.reset(replicaManager)
     val validOffsetCommitResult = commitOffsets(groupId, rebalanceResult.leaderId, rebalanceResult.generation, Map(tp -> offset))
     assertEquals(Errors.NONE, validOffsetCommitResult(tp))
 
-    EasyMock.reset(replicaManager)
     val invalidOffsetCommitResult = commitOffsets(groupId, invalidMemberId, rebalanceResult.generation,
       Map(tp -> offset), Some(leaderInstanceId))
     assertEquals(Errors.FENCED_INSTANCE_ID, invalidOffsetCommitResult(tp))
@@ -1318,7 +1258,6 @@ class GroupCoordinatorTest {
     group.transitionTo(PreparingRebalance)
     group.transitionTo(Empty)
 
-    EasyMock.reset(replicaManager)
 
     // Illegal state exception shall trigger since follower id resides in pending member bucket.
     val expectedException = assertThrows(classOf[IllegalStateException],
@@ -1345,7 +1284,6 @@ class GroupCoordinatorTest {
   def testLeaderRejoinBeforeFinalRebalanceTimeoutWithLongSessionTimeout(): Unit = {
     groupStuckInRebalanceTimeoutDueToNonjoinedStaticMember()
 
-    EasyMock.reset(replicaManager)
     // The static leader should be back now, moving group towards CompletingRebalance
     val leaderRejoinGroupResult = staticJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, leaderInstanceId, protocolType, protocols)
     checkJoinGroupResult(leaderRejoinGroupResult,
@@ -1382,7 +1320,6 @@ class GroupCoordinatorTest {
     assertEquals(Set(dynamicJoinResult.memberId), getGroup(groupId).allDynamicMembers)
 
     // Send a special leave group request from static follower, moving group towards PreparingRebalance
-    EasyMock.reset(replicaManager)
     val followerLeaveGroupResults = singleLeaveGroup(groupId, rebalanceResult.followerId)
     verifyLeaveGroupResult(followerLeaveGroupResults)
     assertGroupState(groupState = PreparingRebalance)
@@ -1409,7 +1346,6 @@ class GroupCoordinatorTest {
       protocolSuperset, Some(newMemberInstanceId))
     assertGroupState(groupState = PreparingRebalance)
 
-    EasyMock.reset(replicaManager)
     val leaderRejoinGroupResult = staticJoinGroup(groupId, leaderId, leaderInstanceId, protocolType, protocolSuperset, clockAdvance = DefaultRebalanceTimeout + 1)
     checkJoinGroupResult(leaderRejoinGroupResult,
       Errors.NONE,
@@ -1443,7 +1379,6 @@ class GroupCoordinatorTest {
     timer.advanceClock(1)
     assertGroupState(groupState = PreparingRebalance)
 
-    EasyMock.reset(replicaManager)
     val oldFollowerRejoinGroupResult = staticJoinGroup(groupId, initialRebalanceResult.followerId, followerInstanceId, protocolType, protocolSuperset, clockAdvance = DefaultRebalanceTimeout + 1)
     val newMemberJoinGroupResult = Await.result(newMemberJoinGroupFuture, Duration(1, TimeUnit.MILLISECONDS))
 
@@ -1471,7 +1406,6 @@ class GroupCoordinatorTest {
   @Test
   def testJoinGroupProtocolTypeIsNotProvidedWhenAnErrorOccurs(): Unit = {
     // JoinGroup(leader)
-    EasyMock.reset(replicaManager)
     val leaderResponseFuture = sendJoinGroup(groupId, "fake-id", protocolType,
       protocolSuperset, Some(leaderInstanceId), DefaultSessionTimeout)
 
@@ -1484,12 +1418,10 @@ class GroupCoordinatorTest {
   @Test
   def testJoinGroupReturnsTheProtocolType(): Unit = {
     // JoinGroup(leader)
-    EasyMock.reset(replicaManager)
     val leaderResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType,
       protocolSuperset, Some(leaderInstanceId), DefaultSessionTimeout)
 
     // JoinGroup(follower)
-    EasyMock.reset(replicaManager)
     val followerResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType,
       protocolSuperset, Some(followerInstanceId), DefaultSessionTimeout)
 
@@ -1537,12 +1469,10 @@ class GroupCoordinatorTest {
                                                    expectedProtocolType: Option[String],
                                                    expectedProtocolName: Option[String]): Unit = {
     // JoinGroup(leader) with the Protocol Type of the group
-    EasyMock.reset(replicaManager)
     val leaderResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, this.protocolType,
       protocolSuperset, Some(leaderInstanceId), DefaultSessionTimeout)
 
     // JoinGroup(follower) with the Protocol Type of the group
-    EasyMock.reset(replicaManager)
     val followerResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, this.protocolType,
       protocolSuperset, Some(followerInstanceId), DefaultSessionTimeout)
 
@@ -1556,7 +1486,6 @@ class GroupCoordinatorTest {
     val followerId = followerJoinGroupResult.memberId
 
     // SyncGroup with the provided Protocol Type and Name
-    EasyMock.reset(replicaManager)
     val leaderSyncGroupResult = syncGroupLeader(groupId, generationId, leaderId,
       Map(leaderId -> Array.empty), protocolType, protocolName)
     assertEquals(expectedError, leaderSyncGroupResult.error)
@@ -1564,7 +1493,6 @@ class GroupCoordinatorTest {
     assertEquals(expectedProtocolName, leaderSyncGroupResult.protocolName)
 
     // SyncGroup with the provided Protocol Type and Name
-    EasyMock.reset(replicaManager)
     val followerSyncGroupResult = syncGroupFollower(groupId, generationId, followerId,
       protocolType, protocolName)
     assertEquals(expectedError, followerSyncGroupResult.error)
@@ -1588,11 +1516,9 @@ class GroupCoordinatorTest {
   private def staticMembersJoinAndRebalance(leaderInstanceId: String,
                                             followerInstanceId: String,
                                             sessionTimeout: Int = DefaultSessionTimeout): RebalanceResult = {
-    EasyMock.reset(replicaManager)
     val leaderResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType,
       protocolSuperset, Some(leaderInstanceId), sessionTimeout)
 
-    EasyMock.reset(replicaManager)
     val followerResponseFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType,
       protocolSuperset, Some(followerInstanceId), sessionTimeout)
     // The goal for two timer advance is to let first group initial join complete and set newMemberAdded flag to false. Next advance is
@@ -1610,19 +1536,16 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, followerJoinGroupResult.error)
     assertEquals(newGeneration, followerJoinGroupResult.generationId)
 
-    EasyMock.reset(replicaManager)
     val leaderId = leaderJoinGroupResult.memberId
     val leaderSyncGroupResult = syncGroupLeader(groupId, leaderJoinGroupResult.generationId, leaderId, Map(leaderId -> Array[Byte]()))
     assertEquals(Errors.NONE, leaderSyncGroupResult.error)
     assertTrue(getGroup(groupId).is(Stable))
 
-    EasyMock.reset(replicaManager)
     val followerId = followerJoinGroupResult.memberId
     val followerSyncGroupResult = syncGroupFollower(groupId, leaderJoinGroupResult.generationId, followerId)
     assertEquals(Errors.NONE, followerSyncGroupResult.error)
     assertTrue(getGroup(groupId).is(Stable))
 
-    EasyMock.reset(replicaManager)
     new RebalanceResult(newGeneration,
       leaderId,
       leaderSyncGroupResult.memberAssignment,
@@ -1702,11 +1625,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, otherMemberId, 1)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, heartbeatResult)
   }
@@ -1720,7 +1641,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedMemberId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
   }
@@ -1734,11 +1654,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedMemberId, 2)
     assertEquals(Errors.ILLEGAL_GENERATION, heartbeatResult)
   }
@@ -1753,11 +1671,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedConsumerId, Map(assignedConsumerId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedConsumerId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
   }
@@ -1772,19 +1688,15 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedConsumerId, Map(assignedConsumerId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getPartition(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)))
-      .andReturn(HostedPartition.None)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getPartition(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)))
+      .thenReturn(HostedPartition.None)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     timer.advanceClock(DefaultSessionTimeout + 100)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedConsumerId, 1)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, heartbeatResult)
   }
@@ -1801,19 +1713,16 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedConsumerId, Map(assignedConsumerId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
     timer.advanceClock(sessionTimeout / 2)
 
-    EasyMock.reset(replicaManager)
     var heartbeatResult = heartbeat(groupId, assignedConsumerId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
 
     timer.advanceClock(sessionTimeout / 2 + 100)
 
-    EasyMock.reset(replicaManager)
     heartbeatResult = heartbeat(groupId, assignedConsumerId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
   }
@@ -1832,19 +1741,16 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
     timer.advanceClock(sessionTimeout / 2)
 
-    EasyMock.reset(replicaManager)
     val commitOffsetResult = commitOffsets(groupId, assignedMemberId, generationId, Map(tp -> offset))
     assertEquals(Errors.NONE, commitOffsetResult(tp))
 
     timer.advanceClock(sessionTimeout / 2 + 100)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedMemberId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
   }
@@ -1859,24 +1765,20 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
     // now have a new member join to trigger a rebalance
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
     timer.advanceClock(500)
 
-    EasyMock.reset(replicaManager)
     var heartbeatResult = heartbeat(groupId, firstMemberId, firstGenerationId)
     assertEquals(Errors.REBALANCE_IN_PROGRESS, heartbeatResult)
 
     // letting the session expire should make the member fall out of the group
     timer.advanceClock(1100)
 
-    EasyMock.reset(replicaManager)
     heartbeatResult = heartbeat(groupId, firstMemberId, firstGenerationId)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, heartbeatResult)
 
@@ -1895,12 +1797,10 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
     // now have a new member join to trigger a rebalance
-    EasyMock.reset(replicaManager)
     val otherMemberSessionTimeout = DefaultSessionTimeout
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
@@ -1908,7 +1808,6 @@ class GroupCoordinatorTest {
     var expectedResultList = List(Errors.REBALANCE_IN_PROGRESS, Errors.REBALANCE_IN_PROGRESS)
     for (expectedResult <- expectedResultList) {
       timer.advanceClock(otherMemberSessionTimeout)
-      EasyMock.reset(replicaManager)
       val heartbeatResult = heartbeat(groupId, firstMemberId, firstGenerationId)
       assertEquals(expectedResult, heartbeatResult)
     }
@@ -1918,13 +1817,11 @@ class GroupCoordinatorTest {
     val otherJoinResult = await(otherJoinFuture, otherMemberSessionTimeout+100)
     val otherMemberId = otherJoinResult.memberId
     val otherGenerationId = otherJoinResult.generationId
-    EasyMock.reset(replicaManager)
     val syncResult = syncGroupLeader(groupId, otherGenerationId, otherMemberId, Map(otherMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncResult.error)
 
     // the unjoined static member should be remained in the group before session timeout.
     assertEquals(Errors.NONE, otherJoinResult.error)
-    EasyMock.reset(replicaManager)
     var heartbeatResult = heartbeat(groupId, firstMemberId, firstGenerationId)
     assertEquals(Errors.ILLEGAL_GENERATION, heartbeatResult)
 
@@ -1933,17 +1830,14 @@ class GroupCoordinatorTest {
     // now session timeout the unjoined member. Still keeping the new member.
     for (expectedResult <- expectedResultList) {
       timer.advanceClock(otherMemberSessionTimeout)
-      EasyMock.reset(replicaManager)
       heartbeatResult = heartbeat(groupId, otherMemberId, otherGenerationId)
       assertEquals(expectedResult, heartbeatResult)
     }
 
-    EasyMock.reset(replicaManager)
     val otherRejoinGroupFuture = sendJoinGroup(groupId, otherMemberId, protocolType, protocols)
     val otherReJoinResult = await(otherRejoinGroupFuture, otherMemberSessionTimeout+100)
     assertEquals(Errors.NONE, otherReJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherRejoinGenerationId = otherReJoinResult.generationId
     val reSyncResult = syncGroupLeader(groupId, otherRejoinGenerationId, otherMemberId, Map(otherMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, reSyncResult.error)
@@ -1952,7 +1846,6 @@ class GroupCoordinatorTest {
     // to verify that no new rebalance is triggered unexpectedly
     for ( _ <-  1 to 20) {
       timer.advanceClock(500)
-      EasyMock.reset(replicaManager)
       heartbeatResult = heartbeat(groupId, otherMemberId, otherRejoinGenerationId)
       assertEquals(Errors.NONE, heartbeatResult)
     }
@@ -1968,12 +1861,10 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedConsumerId, Map())
     assertEquals(Errors.NONE, syncGroupResult.error)
     assertTrue(syncGroupResult.memberAssignment.isEmpty)
 
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedConsumerId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
   }
@@ -2001,12 +1892,10 @@ class GroupCoordinatorTest {
     val generationId = joinGroupResult.generationId
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedConsumerId, Map(assignedConsumerId -> Array[Byte]()))
     val syncGroupError = syncGroupResult.error
     assertEquals(Errors.NONE, syncGroupError)
 
-    EasyMock.reset(replicaManager)
     val unknownMemberId = "blah"
     val unknownMemberSyncResult = syncGroupFollower(groupId, generationId, unknownMemberId)
     assertEquals(Errors.UNKNOWN_MEMBER_ID, unknownMemberSyncResult.error)
@@ -2021,7 +1910,6 @@ class GroupCoordinatorTest {
     val generationId = joinGroupResult.generationId
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     // send the sync group with an invalid generation
     val syncGroupResult = syncGroupLeader(groupId, generationId+1, assignedConsumerId, Map(assignedConsumerId -> Array[Byte]()))
     assertEquals(Errors.ILLEGAL_GENERATION, syncGroupResult.error)
@@ -2039,14 +1927,11 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols)
 
     val joinResult = await(joinFuture, DefaultSessionTimeout+100)
@@ -2061,7 +1946,6 @@ class GroupCoordinatorTest {
     val nextGenerationId = joinResult.generationId
 
     // this shouldn't cause a rebalance since protocol information hasn't changed
-    EasyMock.reset(replicaManager)
     val followerJoinResult = await(sendJoinGroup(groupId, otherJoinResult.memberId, protocolType, protocols), 1)
 
     assertEquals(Errors.NONE, followerJoinResult.error)
@@ -2076,14 +1960,12 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
     // join groups from the leader should force the group to rebalance, which allows the
     // leader to push new assignments when local metadata changes
 
-    EasyMock.reset(replicaManager)
     val secondJoinResult = await(sendJoinGroup(groupId, firstMemberId, protocolType, protocols), 1)
 
     assertEquals(Errors.NONE, secondJoinResult.error)
@@ -2105,7 +1987,6 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, firstJoinResult.error)
 
     // Starting sync group leader
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
     timer.advanceClock(100)
@@ -2119,15 +2000,12 @@ class GroupCoordinatorTest {
     assertEquals(Stable, group.currentState)
 
     // new member initiates join group
-    EasyMock.reset(replicaManager)
     val secondJoinResult = joinGroupPartial(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
     assertEquals(Errors.MEMBER_ID_REQUIRED, secondJoinResult.error)
     assertEquals(1, group.numPending)
     assertEquals(Stable, group.currentState)
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     // advance clock to timeout the pending member
     assertEquals(Set(firstMemberId), group.allMembers)
@@ -2135,7 +2013,6 @@ class GroupCoordinatorTest {
     timer.advanceClock(300)
 
     // original (firstMember) member sends heartbeats to prevent session timeouts.
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, firstMemberId, 1)
     assertEquals(Errors.NONE, heartbeatResult)
 
@@ -2160,40 +2037,33 @@ class GroupCoordinatorTest {
     assertGroupState(groupState = CompletingRebalance)
 
     // now the group is stable, with the one member that joined above
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, joinResult1.generationId, joinResult1.memberId, Map(joinResult1.memberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
     assertGroupState(groupState = Stable)
 
     // start the join for the second member
-    EasyMock.reset(replicaManager)
     val secondJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
     // rejoin the first member back into the group
-    EasyMock.reset(replicaManager)
     val firstJoinFuture = sendJoinGroup(groupId, joinResult1.memberId, protocolType, protocols)
     val firstMemberJoinResult = await(firstJoinFuture, DefaultSessionTimeout+100)
     val secondMemberJoinResult = await(secondJoinFuture, DefaultSessionTimeout+100)
     assertGroupState(groupState = CompletingRebalance)
 
     // stabilize the group
-    EasyMock.reset(replicaManager)
     val secondSyncResult = syncGroupLeader(groupId, firstMemberJoinResult.generationId, joinResult1.memberId, Map(joinResult1.memberId -> Array[Byte]()))
     assertEquals(Errors.NONE, secondSyncResult.error)
     assertGroupState(groupState = Stable)
 
     // re-join an existing member, to transition the group to PreparingRebalance state.
-    EasyMock.reset(replicaManager)
     sendJoinGroup(groupId, firstMemberJoinResult.memberId, protocolType, protocols)
     assertGroupState(groupState = PreparingRebalance)
 
     // create a pending member in the group
-    EasyMock.reset(replicaManager)
     val pendingMember = joinGroupPartial(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, sessionTimeout=100)
     assertEquals(1, groupCoordinator.groupManager.getGroup(groupId).get.numPending)
 
     // re-join the second existing member
-    EasyMock.reset(replicaManager)
     sendJoinGroup(groupId, secondMemberJoinResult.memberId, protocolType, protocols)
     assertGroupState(groupState = PreparingRebalance)
     assertEquals(1, groupCoordinator.groupManager.getGroup(groupId).get.numPending)
@@ -2210,7 +2080,6 @@ class GroupCoordinatorTest {
     val pendingMember = setupGroupWithPendingMember()
 
     // compete join group for the pending member
-    EasyMock.reset(replicaManager)
     val pendingMemberJoinFuture = sendJoinGroup(groupId, pendingMember.memberId, protocolType, protocols)
     await(pendingMemberJoinFuture, DefaultSessionTimeout+100)
 
@@ -2230,9 +2099,7 @@ class GroupCoordinatorTest {
     // Advancing Clock by > 100 (session timeout for third and fourth member)
     // and < 500 (for first and second members). This will force the coordinator to attempt join
     // completion on heartbeat expiration (since we are in PendingRebalance stage).
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
     timer.advanceClock(120)
 
     assertGroupState(groupState = CompletingRebalance)
@@ -2244,7 +2111,6 @@ class GroupCoordinatorTest {
   def testPendingMembersLeavesGroup(): Unit = {
     val pending = setupGroupWithPendingMember()
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, pending.memberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
@@ -2258,7 +2124,6 @@ class GroupCoordinatorTest {
     joinGroupResult: JoinGroupResult,
     expectedError: Errors
   ): Unit = {
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(
       groupId,
       joinGroupResult.memberId,
@@ -2272,7 +2137,6 @@ class GroupCoordinatorTest {
 
     // First JoinRequests
     var futures = 1.to(nbMembers).map { _ =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -2282,7 +2146,6 @@ class GroupCoordinatorTest {
 
     // Second JoinRequests
     futures = memberIds.map { memberId =>
-      EasyMock.reset(replicaManager)
       sendJoinGroup(groupId, memberId, protocolType, protocols,
         None, DefaultSessionTimeout, DefaultRebalanceTimeout, requiredKnownMemberId)
     }
@@ -2311,11 +2174,8 @@ class GroupCoordinatorTest {
     }
 
     // Advance part the rebalance timeout to trigger the delayed operation.
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject()))
-      .andReturn(Some(RecordBatch.MAGIC_VALUE_V1))
-      .anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition]))
+      .thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     timer.advanceClock(DefaultRebalanceTimeout / 2 + 1)
 
@@ -2343,7 +2203,6 @@ class GroupCoordinatorTest {
     }
 
     // Leader sends Sync
-    EasyMock.reset(replicaManager)
     val assignments = results.map(result => result.memberId -> Array.empty[Byte]).toMap
     val leaderResult = sendSyncGroupLeader(groupId, results.head.generationId, results.head.memberId,
       Some(protocolType), Some(protocolName), None, assignments)
@@ -2383,9 +2242,7 @@ class GroupCoordinatorTest {
     }
 
     // Followers send Sync
-    EasyMock.reset(replicaManager)
     val followerResults = results.tail.map { joinGroupResult =>
-      EasyMock.reset(replicaManager)
       sendSyncGroupFollower(groupId, joinGroupResult.generationId, joinGroupResult.memberId,
         Some(protocolType), Some(protocolName), None)
     }
@@ -2421,7 +2278,6 @@ class GroupCoordinatorTest {
       verifyHeartbeat(joinGroupResult, Errors.NONE)
     }
 
-    EasyMock.reset(replicaManager)
     val assignments = results.map(result => result.memberId -> Array.empty[Byte]).toMap
     val leaderResult = sendSyncGroupLeader(groupId, results.head.generationId, results.head.memberId,
       Some(protocolType), Some(protocolName), None, assignments)
@@ -2429,9 +2285,7 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, await(leaderResult, 1).error)
 
     // Followers send Sync
-    EasyMock.reset(replicaManager)
     val followerResults = results.tail.map { joinGroupResult =>
-      EasyMock.reset(replicaManager)
       sendSyncGroupFollower(groupId, joinGroupResult.generationId, joinGroupResult.memberId,
         Some(protocolType), Some(protocolName), None)
     }
@@ -2495,14 +2349,11 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols)
 
     val joinResult = await(joinFuture, DefaultSessionTimeout+100)
@@ -2518,7 +2369,6 @@ class GroupCoordinatorTest {
 
     // with no leader SyncGroup, the follower's request should fail with an error indicating
     // that it should rejoin
-    EasyMock.reset(replicaManager)
     val followerSyncFuture = sendSyncGroupFollower(groupId, nextGenerationId, otherJoinResult.memberId, None, None, None)
 
     timer.advanceClock(DefaultSessionTimeout + 100)
@@ -2539,14 +2389,11 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, firstJoinResult.leaderId)
     assertEquals(Errors.NONE, firstJoinResult.error)
 
-    EasyMock.reset(replicaManager)
     val firstSyncResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, firstSyncResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols)
 
     val joinResult = await(joinFuture, DefaultSessionTimeout+100)
@@ -2564,13 +2411,11 @@ class GroupCoordinatorTest {
     val followerId = otherJoinResult.memberId
     val followerAssignment = Array[Byte](1)
 
-    EasyMock.reset(replicaManager)
     val leaderSyncResult = syncGroupLeader(groupId, nextGenerationId, leaderId,
       Map(leaderId -> leaderAssignment, followerId -> followerAssignment))
     assertEquals(Errors.NONE, leaderSyncResult.error)
     assertEquals(leaderAssignment, leaderSyncResult.memberAssignment)
 
-    EasyMock.reset(replicaManager)
     val followerSyncResult = syncGroupFollower(groupId, nextGenerationId, otherJoinResult.memberId)
     assertEquals(Errors.NONE, followerSyncResult.error)
     assertEquals(followerAssignment, followerSyncResult.memberAssignment)
@@ -2588,14 +2433,11 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, joinGroupResult.leaderId)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, firstGenerationId, firstMemberId, Map(firstMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val otherJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val joinFuture = sendJoinGroup(groupId, firstMemberId, protocolType, protocols)
 
     val joinResult = await(joinFuture, DefaultSessionTimeout+100)
@@ -2613,10 +2455,8 @@ class GroupCoordinatorTest {
     assertEquals(firstMemberId, joinResult.leaderId)
     assertEquals(firstMemberId, otherJoinResult.leaderId)
 
-    EasyMock.reset(replicaManager)
     val followerSyncFuture = sendSyncGroupFollower(groupId, nextGenerationId, followerId, None, None, None)
 
-    EasyMock.reset(replicaManager)
     val leaderSyncResult = syncGroupLeader(groupId, nextGenerationId, leaderId,
       Map(leaderId -> leaderAssignment, followerId -> followerAssignment))
     assertEquals(Errors.NONE, leaderSyncResult.error)
@@ -2660,12 +2500,10 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, joinGroupError)
 
     // and leaves.
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, assignedMemberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
     // The simple offset commit should now fail
-    EasyMock.reset(replicaManager)
     val tp = new TopicPartition("topic", 0)
     val offset = offsetAndMetadata(0)
     val commitOffsetResult = commitOffsets(groupId, OffsetCommitRequest.DEFAULT_MEMBER_ID,
@@ -2721,13 +2559,11 @@ class GroupCoordinatorTest {
     assertEquals(Empty.toString, summary.state)
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val deleteErrors = groupCoordinator.handleDeleteGroups(Set(groupId))
     assertEquals(Errors.NONE, deleteErrors(groupId))
@@ -3063,7 +2899,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val commitOffsetResult = commitOffsets(groupId, assignedMemberId, generationId, Map(tp -> offset))
     assertEquals(Errors.REBALANCE_IN_PROGRESS, commitOffsetResult(tp))
   }
@@ -3079,7 +2914,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val commitOffsetResult = commitOffsets(groupId, memberId, generationId, Map(tp -> offset))
     assertEquals(Errors.UNKNOWN_MEMBER_ID, commitOffsetResult(tp))
   }
@@ -3096,7 +2930,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val commitOffsetResult = commitOffsets(groupId, assignedMemberId, generationId + 1, Map(tp -> offset))
     assertEquals(Errors.ILLEGAL_GENERATION, commitOffsetResult(tp))
   }
@@ -3134,7 +2967,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val invalidIdCommitOffsetResult = commitTransactionalOffsets(groupId, producerId, producerEpoch,
       Map(tp -> offset), "invalid-member")
     assertEquals(Errors.UNKNOWN_MEMBER_ID, invalidIdCommitOffsetResult (tp))
@@ -3152,7 +2984,6 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, joinGroupError)
 
 
-    EasyMock.reset(replicaManager)
     val assignedConsumerId = joinGroupResult.memberId
     val leaderCommitOffsetResult = commitTransactionalOffsets(groupId, producerId, producerEpoch,
       Map(tp -> offset), assignedConsumerId, generationId = joinGroupResult.generationId)
@@ -3170,7 +3001,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
 
     val assignedConsumerId = joinGroupResult.memberId
     val initialGenerationId = joinGroupResult.generationId
@@ -3190,7 +3020,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
 
     val assignedConsumerId = joinGroupResult.memberId
     val initialGenerationId = joinGroupResult.generationId
@@ -3209,11 +3038,9 @@ class GroupCoordinatorTest {
     assertEquals(Errors.NONE, joinGroupError)
 
     // Then join with a new consumer to trigger a rebalance
-    EasyMock.reset(replicaManager)
     sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols)
 
     // We should be in the middle of a rebalance, so the heartbeat should return rebalance in progress
-    EasyMock.reset(replicaManager)
     val heartbeatResult = heartbeat(groupId, assignedConsumerId, initialGenerationId)
     assertEquals(Errors.REBALANCE_IN_PROGRESS, heartbeatResult)
   }
@@ -3227,11 +3054,9 @@ class GroupCoordinatorTest {
     assertEquals(1, initialGenerationId)
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, initialGenerationId, memberId, Map(memberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val joinGroupFuture = sendJoinGroup(groupId, memberId, protocolType, protocols)
     val otherJoinGroupResult = await(joinGroupFuture, 1)
 
@@ -3264,7 +3089,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, otherMemberId)
     verifyLeaveGroupResult(leaveGroupResults, Errors.NONE, List(Errors.UNKNOWN_MEMBER_ID))
   }
@@ -3297,7 +3121,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, assignedMemberId)
     verifyLeaveGroupResult(leaveGroupResults)
   }
@@ -3307,7 +3130,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = staticJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, leaderInstanceId, protocolType, protocolSuperset)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, "some_member", Some(leaderInstanceId))
     verifyLeaveGroupResult(leaveGroupResults, Errors.NONE, List(Errors.FENCED_INSTANCE_ID))
   }
@@ -3317,7 +3139,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = staticJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, leaderInstanceId, protocolType, protocolSuperset)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     // Having unknown member id will not affect the request processing.
     val leaveGroupResults = singleLeaveGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, Some(leaderInstanceId))
     verifyLeaveGroupResult(leaveGroupResults, Errors.NONE, List(Errors.NONE))
@@ -3378,7 +3199,6 @@ class GroupCoordinatorTest {
   def testPendingMemberBatchLeaveGroup(): Unit = {
     val pendingMember = setupGroupWithPendingMember()
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = batchLeaveGroup(groupId, List(new MemberIdentity()
       .setGroupInstanceId("unknown-instance"), new MemberIdentity()
       .setMemberId(pendingMember.memberId)))
@@ -3394,7 +3214,6 @@ class GroupCoordinatorTest {
     val generationId = joinGroupResult.generationId
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
@@ -3436,7 +3255,6 @@ class GroupCoordinatorTest {
     assertEquals(0, groups2.size)
 
     // Member syncs
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
@@ -3449,7 +3267,6 @@ class GroupCoordinatorTest {
     assertEquals(0, groups4.size)
 
     // Member leaves
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, assignedMemberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
@@ -3464,14 +3281,12 @@ class GroupCoordinatorTest {
 
   @Test
   def testDescribeGroupWrongCoordinator(): Unit = {
-    EasyMock.reset(replicaManager)
     val (error, _) = groupCoordinator.handleDescribeGroup(otherGroupId)
     assertEquals(Errors.NOT_COORDINATOR, error)
   }
 
   @Test
   def testDescribeGroupInactiveGroup(): Unit = {
-    EasyMock.reset(replicaManager)
     val (error, summary) = groupCoordinator.handleDescribeGroup(groupId)
     assertEquals(Errors.NONE, error)
     assertEquals(GroupCoordinator.DeadGroup, summary)
@@ -3485,11 +3300,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val (error, summary) = groupCoordinator.handleDescribeGroup(groupId)
     assertEquals(Errors.NONE, error)
     assertEquals(protocolType, summary.protocolType)
@@ -3505,11 +3318,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val (error, summary) = groupCoordinator.handleDescribeGroup(groupId)
     assertEquals(Errors.NONE, error)
     assertEquals(protocolType, summary.protocolType)
@@ -3525,7 +3336,6 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val (error, summary) = groupCoordinator.handleDescribeGroup(groupId)
     assertEquals(Errors.NONE, error)
     assertEquals(protocolType, summary.protocolType)
@@ -3563,18 +3373,15 @@ class GroupCoordinatorTest {
     val memberId = JoinGroupRequest.UNKNOWN_MEMBER_ID
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, joinGroupResult.memberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val result = groupCoordinator.handleDeleteGroups(Set(groupId))
     assert(result.size == 1 && result.contains(groupId) && result.get(groupId).contains(Errors.NONE))
@@ -3589,11 +3396,9 @@ class GroupCoordinatorTest {
     val joinGroupError = joinGroupResult.error
     assertEquals(Errors.NONE, joinGroupError)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, assignedMemberId, Map(assignedMemberId -> Array[Byte]()))
     assertEquals(Errors.NONE, syncGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val tp = new TopicPartition("topic", 0)
     val offset = offsetAndMetadata(0)
     val commitOffsetResult = commitOffsets(groupId, assignedMemberId, joinGroupResult.generationId, Map(tp -> offset))
@@ -3603,18 +3408,15 @@ class GroupCoordinatorTest {
     assertEquals(Stable.toString, describeGroupResult._2.state)
     assertEquals(assignedMemberId, describeGroupResult._2.members.head.memberId)
 
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, assignedMemberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val result = groupCoordinator.handleDeleteGroups(Set(groupId))
     assert(result.size == 1 && result.contains(groupId) && result.get(groupId).contains(Errors.NONE))
@@ -3652,7 +3454,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, "My Protocol", protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, joinGroupResult.leaderId, Map.empty)
     assertEquals(Errors.NONE, syncGroupResult.error)
 
@@ -3660,27 +3461,23 @@ class GroupCoordinatorTest {
     val t2p0 = new TopicPartition("bar", 0)
     val offset = offsetAndMetadata(37)
 
-    EasyMock.reset(replicaManager)
     val validOffsetCommitResult = commitOffsets(groupId, joinGroupResult.memberId, joinGroupResult.generationId,
       Map(t1p0 -> offset, t2p0 -> offset))
     assertEquals(Errors.NONE, validOffsetCommitResult(t1p0))
     assertEquals(Errors.NONE, validOffsetCommitResult(t2p0))
 
     // and leaves.
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, joinGroupResult.memberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
     assertTrue(groupCoordinator.groupManager.getGroup(groupId).exists(_.is(Empty)))
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val (groupError, topics) = groupCoordinator.handleDeleteOffsets(groupId, Seq(t1p0),
       RequestLocal.NoCaching)
@@ -3700,14 +3497,12 @@ class GroupCoordinatorTest {
     val memberId = JoinGroupRequest.UNKNOWN_MEMBER_ID
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, joinGroupResult.leaderId, Map.empty)
     assertEquals(Errors.NONE, syncGroupResult.error)
 
     val tp = new TopicPartition("foo", 0)
     val offset = offsetAndMetadata(37)
 
-    EasyMock.reset(replicaManager)
     val validOffsetCommitResult = commitOffsets(groupId, joinGroupResult.memberId, joinGroupResult.generationId,
       Map(tp -> offset))
     assertEquals(Errors.NONE, validOffsetCommitResult(tp))
@@ -3741,7 +3536,6 @@ class GroupCoordinatorTest {
     val joinGroupResult = dynamicJoinGroup(groupId, memberId, protocolType, protocols)
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, joinGroupResult.leaderId, Map.empty)
     assertEquals(Errors.NONE, syncGroupResult.error)
 
@@ -3749,27 +3543,23 @@ class GroupCoordinatorTest {
     val t2p0 = new TopicPartition("bar", 0)
     val offset = offsetAndMetadata(37)
 
-    EasyMock.reset(replicaManager)
     val validOffsetCommitResult = commitOffsets(groupId, joinGroupResult.memberId, joinGroupResult.generationId,
       Map(t1p0 -> offset, t2p0 -> offset))
     assertEquals(Errors.NONE, validOffsetCommitResult(t1p0))
     assertEquals(Errors.NONE, validOffsetCommitResult(t2p0))
 
     // and leaves.
-    EasyMock.reset(replicaManager)
     val leaveGroupResults = singleLeaveGroup(groupId, joinGroupResult.memberId)
     verifyLeaveGroupResult(leaveGroupResults)
 
     assertTrue(groupCoordinator.groupManager.getGroup(groupId).exists(_.is(Empty)))
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val (groupError, topics) = groupCoordinator.handleDeleteOffsets(groupId, Seq(t1p0),
       RequestLocal.NoCaching)
@@ -3794,7 +3584,6 @@ class GroupCoordinatorTest {
       List(("protocol", ConsumerProtocol.serializeSubscription(subscription).array())))
     assertEquals(Errors.NONE, joinGroupResult.error)
 
-    EasyMock.reset(replicaManager)
     val syncGroupResult = syncGroupLeader(groupId, joinGroupResult.generationId, joinGroupResult.leaderId, Map.empty)
     assertEquals(Errors.NONE, syncGroupResult.error)
 
@@ -3802,7 +3591,6 @@ class GroupCoordinatorTest {
     val t2p0 = new TopicPartition("bar", 0)
     val offset = offsetAndMetadata(37)
 
-    EasyMock.reset(replicaManager)
     val validOffsetCommitResult = commitOffsets(groupId, joinGroupResult.memberId, joinGroupResult.generationId,
       Map(t1p0 -> offset, t2p0 -> offset))
     assertEquals(Errors.NONE, validOffsetCommitResult(t1p0))
@@ -3811,13 +3599,11 @@ class GroupCoordinatorTest {
     assertTrue(groupCoordinator.groupManager.getGroup(groupId).exists(_.is(Stable)))
 
     val groupTopicPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)
-    val partition: Partition = EasyMock.niceMock(classOf[Partition])
+    val partition: Partition = mock(classOf[Partition])
 
-    EasyMock.reset(replicaManager)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
-    EasyMock.replay(replicaManager, partition)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
 
     val (groupError, topics) = groupCoordinator.handleDeleteOffsets(groupId, Seq(t1p0, t2p0),
       RequestLocal.NoCaching)
@@ -3852,10 +3638,8 @@ class GroupCoordinatorTest {
   def shouldResetRebalanceDelayWhenNewMemberJoinsGroupInInitialRebalance(): Unit = {
     val rebalanceTimeout = GroupInitialRebalanceDelay * 3
     val firstMemberJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
-    EasyMock.reset(replicaManager)
     timer.advanceClock(GroupInitialRebalanceDelay - 1)
     val secondMemberJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
-    EasyMock.reset(replicaManager)
     timer.advanceClock(2)
 
     // advance past initial rebalance delay and make sure that tasks
@@ -3876,13 +3660,10 @@ class GroupCoordinatorTest {
   def shouldDelayRebalanceUptoRebalanceTimeout(): Unit = {
     val rebalanceTimeout = GroupInitialRebalanceDelay * 2
     val firstMemberJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
-    EasyMock.reset(replicaManager)
     val secondMemberJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
     timer.advanceClock(GroupInitialRebalanceDelay + 1)
-    EasyMock.reset(replicaManager)
     val thirdMemberJoinFuture = sendJoinGroup(groupId, JoinGroupRequest.UNKNOWN_MEMBER_ID, protocolType, protocols, rebalanceTimeout = rebalanceTimeout)
     timer.advanceClock(GroupInitialRebalanceDelay)
-    EasyMock.reset(replicaManager)
 
     verifyDelayedTaskNotCompleted(firstMemberJoinFuture)
     verifyDelayedTaskNotCompleted(secondMemberJoinFuture)
@@ -3972,8 +3753,7 @@ class GroupCoordinatorTest {
                             requireKnownMemberId: Boolean = false): Future[JoinGroupResult] = {
     val (responseFuture, responseCallback) = setupJoinGroupCallback
 
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     groupCoordinator.handleJoinGroup(groupId, memberId, groupInstanceId,
       requireKnownMemberId, "clientId", "clientHost", rebalanceTimeout, sessionTimeout, protocolType, protocols, responseCallback)
@@ -3991,25 +3771,25 @@ class GroupCoordinatorTest {
                                                  requireKnownMemberId: Boolean = false): Future[JoinGroupResult] = {
     val (responseFuture, responseCallback) = setupJoinGroupCallback
 
-    val capturedArgument: Capture[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject()
-    )).andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = capturedArgument.getValue.apply(
+    val capturedArgument: ArgumentCaptor[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[scala.collection.Map[TopicPartition, PartitionResponse] => Unit])
+
+    when(replicaManager.appendRecords(anyLong,
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any()
+    )).thenAnswer(_ => {
+      capturedArgument.getValue.apply(
         Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId) ->
           new PartitionResponse(appendRecordError, 0L, RecordBatch.NO_TIMESTAMP, 0L)
-      ))
+       )
+      )
     })
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     groupCoordinator.handleJoinGroup(groupId, memberId, Some(groupInstanceId),
       requireKnownMemberId, "clientId", "clientHost", rebalanceTimeout, sessionTimeout, protocolType, protocols, responseCallback)
@@ -4025,24 +3805,25 @@ class GroupCoordinatorTest {
                                   assignment: Map[String, Array[Byte]]): Future[SyncGroupResult] = {
     val (responseFuture, responseCallback) = setupSyncGroupCallback
 
-    val capturedArgument: Capture[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())).andAnswer(new IAnswer[Unit] {
-      override def answer = capturedArgument.getValue.apply(
-        Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId) ->
-          new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)
+    val capturedArgument: ArgumentCaptor[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[scala.collection.Map[TopicPartition, PartitionResponse] => Unit])
+
+    when(replicaManager.appendRecords(anyLong,
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())).thenAnswer(_ => {
+        capturedArgument.getValue.apply(
+          Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId) ->
+            new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)
+          )
         )
-      )})
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+      }
+    )
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     groupCoordinator.handleSyncGroup(groupId, generation, leaderId, protocolType, protocolName,
       groupInstanceId, assignment, responseCallback)
@@ -4057,7 +3838,6 @@ class GroupCoordinatorTest {
                                     groupInstanceId: Option[String] = None): Future[SyncGroupResult] = {
     val (responseFuture, responseCallback) = setupSyncGroupCallback
 
-    EasyMock.replay(replicaManager)
 
     groupCoordinator.handleSyncGroup(groupId, generation, memberId,
       prototolType, prototolName, groupInstanceId, Map.empty[String, Array[Byte]], responseCallback)
@@ -4080,7 +3860,6 @@ class GroupCoordinatorTest {
       if (joinGroupResult.error != Errors.MEMBER_ID_REQUIRED) {
         return joinGroupResult
       }
-      EasyMock.reset(replicaManager)
       responseFuture = sendJoinGroup(groupId, joinGroupResult.memberId, protocolType, protocols, None, sessionTimeout, rebalanceTimeout, requireKnownMemberId)
     }
     timer.advanceClock(GroupInitialRebalanceDelay + 1)
@@ -4151,7 +3930,6 @@ class GroupCoordinatorTest {
                         groupInstanceId: Option[String] = None): HeartbeatCallbackParams = {
     val (responseFuture, responseCallback) = setupHeartbeatCallback
 
-    EasyMock.replay(replicaManager)
 
     groupCoordinator.handleHeartbeat(groupId, consumerId, groupInstanceId, generationId, responseCallback)
     Await.result(responseFuture, Duration(40, TimeUnit.MILLISECONDS))
@@ -4168,26 +3946,25 @@ class GroupCoordinatorTest {
                             groupInstanceId: Option[String] = None): CommitOffsetCallbackParams = {
     val (responseFuture, responseCallback) = setupCommitOffsetsCallback
 
-    val capturedArgument: Capture[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(new IAnswer[Unit] {
-      override def answer = capturedArgument.getValue.apply(
-          Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId) ->
-            new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)
-          )
+    val capturedArgument: ArgumentCaptor[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[scala.collection.Map[TopicPartition, PartitionResponse] => Unit])
+
+    when(replicaManager.appendRecords(anyLong,
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    ).thenAnswer(_ => {
+      capturedArgument.getValue.apply(
+        Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId) ->
+          new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)
         )
-      })
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+      )
+    })
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     groupCoordinator.handleCommitOffsets(groupId, memberId, groupInstanceId, generationId, offsets, responseCallback)
     Await.result(responseFuture, Duration(40, TimeUnit.MILLISECONDS))
@@ -4202,30 +3979,29 @@ class GroupCoordinatorTest {
                                          generationId: Int = JoinGroupRequest.UNKNOWN_GENERATION_ID) = {
     val (responseFuture, responseCallback) = setupCommitOffsetsCallback
 
-    val capturedArgument: Capture[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(new IAnswer[Unit] {
-      override def answer = capturedArgument.getValue.apply(
+    val capturedArgument: ArgumentCaptor[scala.collection.Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[scala.collection.Map[TopicPartition, PartitionResponse] => Unit])
+
+    when(replicaManager.appendRecords(anyLong,
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    ).thenAnswer(_ => {
+      capturedArgument.getValue.apply(
         Map(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupCoordinator.partitionFor(groupId)) ->
           new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)
         )
-      )})
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V2)).anyTimes()
-    EasyMock.replay(replicaManager)
+      )
+    })
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V2))
 
     groupCoordinator.handleTxnCommitOffsets(groupId, producerId, producerEpoch,
       memberId, groupInstanceId, generationId, offsets, responseCallback)
     val result = Await.result(responseFuture, Duration(40, TimeUnit.MILLISECONDS))
-    EasyMock.reset(replicaManager)
     result
   }
 
@@ -4243,10 +4019,9 @@ class GroupCoordinatorTest {
                               memberIdentities: List[MemberIdentity]): LeaveGroupResult = {
     val (responseFuture, responseCallback) = setupLeaveGroupCallback
 
-    EasyMock.expect(replicaManager.getPartition(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)))
-      .andReturn(HostedPartition.None)
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(Some(RecordBatch.MAGIC_VALUE_V1)).anyTimes()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getPartition(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId)))
+      .thenReturn(HostedPartition.None)
+    when(replicaManager.getMagic(any[TopicPartition])).thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
 
     groupCoordinator.handleLeaveGroup(groupId, memberIdentities, responseCallback)
     Await.result(responseFuture, Duration(40, TimeUnit.MILLISECONDS))
diff --git a/core/src/test/scala/unit/kafka/coordinator/group/GroupMetadataManagerTest.scala b/core/src/test/scala/unit/kafka/coordinator/group/GroupMetadataManagerTest.scala
index 5fe4bf9..7132ca7 100644
--- a/core/src/test/scala/unit/kafka/coordinator/group/GroupMetadataManagerTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/group/GroupMetadataManagerTest.scala
@@ -27,7 +27,7 @@ import javax.management.ObjectName
 import kafka.api._
 import kafka.cluster.Partition
 import kafka.common.OffsetAndMetadata
-import kafka.log.{AppendOrigin, UnifiedLog, LogAppendInfo}
+import kafka.log.{AppendOrigin, LogAppendInfo, UnifiedLog}
 import kafka.metrics.KafkaYammerMetrics
 import kafka.server.{FetchDataInfo, FetchLogEnd, HostedPartition, KafkaConfig, LogOffsetMetadata, ReplicaManager, RequestLocal}
 import kafka.utils.{KafkaScheduler, MockTime, TestUtils}
@@ -42,9 +42,11 @@ import org.apache.kafka.common.record._
 import org.apache.kafka.common.requests.OffsetFetchResponse
 import org.apache.kafka.common.requests.ProduceResponse.PartitionResponse
 import org.apache.kafka.common.utils.Utils
-import org.easymock.{Capture, EasyMock, IAnswer}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.ArgumentMatchers.{any, anyInt, anyLong, anyShort}
+import org.mockito.Mockito.{mock, reset, times, verify, when}
 
 import scala.jdk.CollectionConverters._
 import scala.collection._
@@ -88,11 +90,11 @@ class GroupMetadataManagerTest {
     defaultOffsetRetentionMs = offsetConfig.offsetsRetentionMs
     metrics = new kMetrics()
     time = new MockTime
-    replicaManager = EasyMock.createNiceMock(classOf[ReplicaManager])
+    replicaManager = mock(classOf[ReplicaManager])
     groupMetadataManager = new GroupMetadataManager(0, ApiVersion.latestVersion, offsetConfig, replicaManager,
       time, metrics)
     groupMetadataManager.startup(() => numOffsetsPartitions, false)
-    partition = EasyMock.niceMock(classOf[Partition])
+    partition = mock(classOf[Partition])
   }
 
   @AfterEach
@@ -140,8 +142,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.withRecords(startOffset, CompressionType.NONE, offsetCommitRecords.toArray: _*)
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -173,8 +173,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -211,8 +209,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -246,8 +242,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     // Since there are no committed offsets for the group, and there is no other group metadata, we don't expect the
@@ -279,8 +273,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     // The group should be loaded with pending offsets.
@@ -327,8 +319,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -383,8 +373,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -447,8 +435,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -494,8 +480,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     // The group should be loaded with pending offsets.
@@ -537,8 +521,6 @@ class GroupMetadataManagerTest {
     val records = MemoryRecords.readableRecords(buffer)
     expectGroupMetadataLoad(groupMetadataTopicPartition, 0, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     // The group should be loaded with pending offsets.
@@ -622,8 +604,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -662,8 +642,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -743,7 +721,6 @@ class GroupMetadataManagerTest {
     assertEquals(groupEpoch, groupMetadataManager.epochForPartitionId.get(groupTopicPartition.partition()),
     "Replica which was stopped still in epochForPartitionId")
 
-    EasyMock.reset(replicaManager)
     loadOffsetsAndGroup(groupTopicPartition, groupEpoch + 1)
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
     assertEquals(initiallyLoaded.groupId, group.groupId)
@@ -774,8 +751,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     assertEquals(None, groupMetadataManager.getGroup(groupId))
@@ -804,8 +779,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -823,18 +796,19 @@ class GroupMetadataManagerTest {
     val endOffset = 10L
     val groupEpoch = 2
 
-    val logMock: UnifiedLog = EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(replicaManager.getLog(groupTopicPartition)).andStubReturn(Some(logMock))
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    when(replicaManager.getLog(groupTopicPartition)).thenReturn(Some(logMock))
     expectGroupMetadataLoad(logMock, startOffset, MemoryRecords.EMPTY)
-    EasyMock.expect(replicaManager.getLogEndOffset(groupTopicPartition)).andStubReturn(Some(endOffset))
-    EasyMock.replay(logMock)
-
-    EasyMock.replay(replicaManager)
-
+    when(replicaManager.getLogEndOffset(groupTopicPartition)).thenReturn(Some(endOffset))
     groupMetadataManager.loadGroupsAndOffsets(groupTopicPartition, groupEpoch, _ => (), 0L)
 
-    EasyMock.verify(logMock)
-    EasyMock.verify(replicaManager)
+    verify(logMock).logStartOffset
+    verify(logMock).read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true))
+    verify(replicaManager).getLog(groupTopicPartition)
+    verify(replicaManager, times(2)).getLogEndOffset(groupTopicPartition)
 
     assertFalse(groupMetadataManager.isPartitionLoading(groupTopicPartition.partition()))
   }
@@ -866,8 +840,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -891,26 +863,54 @@ class GroupMetadataManagerTest {
     val tp2 = new TopicPartition("bar", 0)
     val tp3 = new TopicPartition("xxx", 0)
 
-    val logMock: UnifiedLog = EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(replicaManager.getLog(groupTopicPartition)).andStubReturn(Some(logMock))
+    val fileRecordsMock: FileRecords = mock(classOf[FileRecords])
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    when(replicaManager.getLog(groupTopicPartition)).thenReturn(Some(logMock))
 
     val segment1MemberId = "a"
     val segment1Offsets = Map(tp0 -> 23L, tp1 -> 455L, tp3 -> 42L)
     val segment1Records = MemoryRecords.withRecords(startOffset, CompressionType.NONE,
       (createCommittedOffsetRecords(segment1Offsets) ++ Seq(buildStableGroupRecordWithMember(
         generation, protocolType, protocol, segment1MemberId))).toArray: _*)
-    val segment1End = expectGroupMetadataLoad(logMock, startOffset, segment1Records)
+    val segment1End = startOffset + segment1Records.records.asScala.size
 
     val segment2MemberId = "b"
     val segment2Offsets = Map(tp0 -> 33L, tp2 -> 8992L, tp3 -> 10L)
     val segment2Records = MemoryRecords.withRecords(segment1End, CompressionType.NONE,
       (createCommittedOffsetRecords(segment2Offsets) ++ Seq(buildStableGroupRecordWithMember(
         generation, protocolType, protocol, segment2MemberId))).toArray: _*)
-    val segment2End = expectGroupMetadataLoad(logMock, segment1End, segment2Records)
-
-    EasyMock.expect(replicaManager.getLogEndOffset(groupTopicPartition)).andStubReturn(Some(segment2End))
+    val segment2End = segment1End + segment2Records.records.asScala.size
+
+    when(logMock.logStartOffset)
+      .thenReturn(segment1End)
+      .thenReturn(segment2End)
+    when(logMock.read(ArgumentMatchers.eq(segment1End),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(segment1End), fileRecordsMock))
+    when(logMock.read(ArgumentMatchers.eq(segment2End),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(segment2End), fileRecordsMock))
+    when(fileRecordsMock.sizeInBytes())
+      .thenReturn(segment1Records.sizeInBytes)
+      .thenReturn(segment2Records.sizeInBytes)
+
+    val bufferCapture: ArgumentCaptor[ByteBuffer] = ArgumentCaptor.forClass(classOf[ByteBuffer])
+    when(fileRecordsMock.readInto(bufferCapture.capture(), anyInt()))
+      .thenAnswer(_ => {
+      val buffer = bufferCapture.getValue
+      buffer.put(segment1Records.buffer.duplicate)
+      buffer.flip()
+    }).thenAnswer(_ => {
+      val buffer = bufferCapture.getValue
+      buffer.put(segment2Records.buffer.duplicate)
+      buffer.flip()
+    })
 
-    EasyMock.replay(logMock, replicaManager)
+    when(replicaManager.getLogEndOffset(groupTopicPartition)).thenReturn(Some(segment2End))
 
     groupMetadataManager.loadGroupsAndOffsets(groupTopicPartition, groupEpoch, _ => (), 0L)
 
@@ -1099,8 +1099,6 @@ class GroupMetadataManagerTest {
     groupMetadataManager.addGroup(group)
 
     val capturedRecords = expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var maybeError: Option[Errors] = None
     def callback(error: Errors): Unit = {
       maybeError = Some(error)
@@ -1108,7 +1106,6 @@ class GroupMetadataManagerTest {
 
     groupMetadataManager.storeGroup(group, Map.empty, callback)
     assertEquals(Some(Errors.NONE), maybeError)
-    assertTrue(capturedRecords.hasCaptured)
     val records = capturedRecords.getValue()(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId))
         .records.asScala.toList
     assertEquals(1, records.size)
@@ -1126,8 +1123,6 @@ class GroupMetadataManagerTest {
     groupMetadataManager.addGroup(group)
 
     val capturedRecords = expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var maybeError: Option[Errors] = None
     def callback(error: Errors): Unit = {
       maybeError = Some(error)
@@ -1135,9 +1130,6 @@ class GroupMetadataManagerTest {
 
     groupMetadataManager.storeGroup(group, Map.empty, callback)
     assertEquals(Some(Errors.NONE), maybeError)
-    assertTrue(capturedRecords.hasCaptured)
-
-    assertTrue(capturedRecords.hasCaptured)
     val records = capturedRecords.getValue()(new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, groupPartitionId))
       .records.asScala.toList
     assertEquals(1, records.size)
@@ -1163,14 +1155,11 @@ class GroupMetadataManagerTest {
   }
 
   private def assertStoreGroupErrorMapping(appendError: Errors, expectedError: Errors): Unit = {
-    EasyMock.reset(replicaManager)
-
+    reset(replicaManager)
     val group = new GroupMetadata(groupId, Empty, time)
     groupMetadataManager.addGroup(group)
 
     expectAppendMessage(appendError)
-    EasyMock.replay(replicaManager)
-
     var maybeError: Option[Errors] = None
     def callback(error: Errors): Unit = {
       maybeError = Some(error)
@@ -1179,7 +1168,16 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeGroup(group, Map.empty, callback)
     assertEquals(Some(expectedError), maybeError)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
@@ -1198,8 +1196,6 @@ class GroupMetadataManagerTest {
     group.initNextGeneration()
 
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var maybeError: Option[Errors] = None
     def callback(error: Errors): Unit = {
       maybeError = Some(error)
@@ -1208,12 +1204,21 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeGroup(group, Map(memberId -> Array[Byte]()), callback)
     assertEquals(Some(Errors.NONE), maybeError)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
   def testStoreNonEmptyGroupWhenCoordinatorHasMoved(): Unit = {
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(None)
+    when(replicaManager.getMagic(any())).thenReturn(None)
     val memberId = "memberId"
     val clientId = "clientId"
     val clientHost = "localhost"
@@ -1226,8 +1231,6 @@ class GroupMetadataManagerTest {
     group.transitionTo(PreparingRebalance)
     group.initNextGeneration()
 
-    EasyMock.replay(replicaManager)
-
     var maybeError: Option[Errors] = None
     def callback(error: Errors): Unit = {
       maybeError = Some(error)
@@ -1236,7 +1239,7 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeGroup(group, Map(memberId -> Array[Byte]()), callback)
     assertEquals(Some(Errors.NOT_COORDINATOR), maybeError)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
@@ -1253,8 +1256,6 @@ class GroupMetadataManagerTest {
     val offsets = immutable.Map(topicPartition -> OffsetAndMetadata(offset, "", time.milliseconds()))
 
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1277,7 +1278,15 @@ class GroupMetadataManagerTest {
     assertEquals(Errors.NONE, partitionResponse.error)
     assertEquals(offset, partitionResponse.offset)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
     // Will update sensor after commit
     assertEquals(1, TestUtils.totalMetricValue(metrics, "offset-commit-count"))
   }
@@ -1298,9 +1307,8 @@ class GroupMetadataManagerTest {
     val offsetAndMetadata = OffsetAndMetadata(offset, "", time.milliseconds())
     val offsets = immutable.Map(topicPartition -> offsetAndMetadata)
 
-    val capturedResponseCallback = appendAndCaptureCallback()
-    EasyMock.replay(replicaManager)
-
+    val capturedResponseCallback: ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, PartitionResponse] => Unit])
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1309,6 +1317,17 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeOffsets(group, memberId, offsets, callback, producerId, producerEpoch)
     assertTrue(group.hasOffsets)
     assertTrue(group.allOffsets.isEmpty)
+
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedResponseCallback.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
     capturedResponseCallback.getValue.apply(Map(groupTopicPartition ->
       new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
 
@@ -1319,8 +1338,6 @@ class GroupMetadataManagerTest {
     assertTrue(group.hasOffsets)
     assertFalse(group.allOffsets.isEmpty)
     assertEquals(Some(offsetAndMetadata), group.offset(topicPartition))
-
-    EasyMock.verify(replicaManager)
   }
 
   @Test
@@ -1338,9 +1355,8 @@ class GroupMetadataManagerTest {
 
     val offsets = immutable.Map(topicPartition -> OffsetAndMetadata(offset, "", time.milliseconds()))
 
-    val capturedResponseCallback = appendAndCaptureCallback()
-    EasyMock.replay(replicaManager)
 
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1349,6 +1365,7 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeOffsets(group, memberId, offsets, callback, producerId, producerEpoch)
     assertTrue(group.hasOffsets)
     assertTrue(group.allOffsets.isEmpty)
+    val capturedResponseCallback = verifyAppendAndCaptureCallback()
     capturedResponseCallback.getValue.apply(Map(groupTopicPartition ->
       new PartitionResponse(Errors.NOT_ENOUGH_REPLICAS, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
 
@@ -1359,7 +1376,16 @@ class GroupMetadataManagerTest {
     assertFalse(group.hasOffsets)
     assertTrue(group.allOffsets.isEmpty)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
@@ -1377,8 +1403,7 @@ class GroupMetadataManagerTest {
 
     val offsets = immutable.Map(topicPartition -> OffsetAndMetadata(offset, "", time.milliseconds()))
 
-    val capturedResponseCallback = appendAndCaptureCallback()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
 
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
@@ -1388,6 +1413,7 @@ class GroupMetadataManagerTest {
     groupMetadataManager.storeOffsets(group, memberId, offsets, callback, producerId, producerEpoch)
     assertTrue(group.hasOffsets)
     assertTrue(group.allOffsets.isEmpty)
+    val capturedResponseCallback = verifyAppendAndCaptureCallback()
     capturedResponseCallback.getValue.apply(Map(groupTopicPartition ->
       new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
 
@@ -1398,12 +1424,21 @@ class GroupMetadataManagerTest {
     assertFalse(group.hasOffsets)
     assertTrue(group.allOffsets.isEmpty)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
   def testCommitOffsetWhenCoordinatorHasMoved(): Unit = {
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andReturn(None)
+    when(replicaManager.getMagic(any())).thenReturn(None)
     val memberId = ""
     val topicPartition = new TopicPartition("foo", 0)
     val offset = 37
@@ -1415,8 +1450,6 @@ class GroupMetadataManagerTest {
 
     val offsets = immutable.Map(topicPartition -> OffsetAndMetadata(offset, "", time.milliseconds()))
 
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1428,7 +1461,7 @@ class GroupMetadataManagerTest {
     val maybeError = commitErrors.get.get(topicPartition)
     assertEquals(Some(Errors.NOT_COORDINATOR), maybeError)
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).getMagic(any())
   }
 
   @Test
@@ -1444,8 +1477,7 @@ class GroupMetadataManagerTest {
   }
 
   private def assertCommitOffsetErrorMapping(appendError: Errors, expectedError: Errors): Unit = {
-    EasyMock.reset(replicaManager)
-
+    reset(replicaManager)
     val memberId = ""
     val topicPartition = new TopicPartition("foo", 0)
     val offset = 37
@@ -1457,8 +1489,7 @@ class GroupMetadataManagerTest {
 
     val offsets = immutable.Map(topicPartition -> OffsetAndMetadata(offset, "", time.milliseconds()))
 
-    val capturedResponseCallback = appendAndCaptureCallback()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
 
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
@@ -1468,6 +1499,7 @@ class GroupMetadataManagerTest {
     assertEquals(0, TestUtils.totalMetricValue(metrics, "offset-commit-count"))
     groupMetadataManager.storeOffsets(group, memberId, offsets, callback)
     assertTrue(group.hasOffsets)
+    val capturedResponseCallback = verifyAppendAndCaptureCallback()
     capturedResponseCallback.getValue.apply(Map(groupTopicPartition ->
       new PartitionResponse(appendError, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
 
@@ -1479,15 +1511,13 @@ class GroupMetadataManagerTest {
     val cachedOffsets = groupMetadataManager.getOffsets(groupId, defaultRequireStable, Some(Seq(topicPartition)))
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).getMagic(any())
     // Will not update sensor if failed
     assertEquals(0, TestUtils.totalMetricValue(metrics, "offset-commit-count"))
   }
 
   @Test
   def testCommitOffsetPartialFailure(): Unit = {
-    EasyMock.reset(replicaManager)
-
     val memberId = ""
     val topicPartition = new TopicPartition("foo", 0)
     val topicPartitionFailed = new TopicPartition("foo", 1)
@@ -1504,8 +1534,7 @@ class GroupMetadataManagerTest {
       topicPartitionFailed -> OffsetAndMetadata(offset, "s" * (offsetConfig.maxMetadataSize + 1) , time.milliseconds())
     )
 
-    val capturedResponseCallback = appendAndCaptureCallback()
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
 
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
@@ -1515,6 +1544,7 @@ class GroupMetadataManagerTest {
     assertEquals(0, TestUtils.totalMetricValue(metrics, "offset-commit-count"))
     groupMetadataManager.storeOffsets(group, memberId, offsets, callback)
     assertTrue(group.hasOffsets)
+    val capturedResponseCallback = verifyAppendAndCaptureCallback()
     capturedResponseCallback.getValue.apply(Map(groupTopicPartition ->
       new PartitionResponse(Errors.NONE, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
 
@@ -1527,7 +1557,16 @@ class GroupMetadataManagerTest {
     assertEquals(Some(offset), cachedOffsets.get(topicPartition).map(_.offset))
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartitionFailed).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager).getMagic(any())
     assertEquals(1, TestUtils.totalMetricValue(metrics, "offset-commit-count"))
   }
 
@@ -1585,8 +1624,6 @@ class GroupMetadataManagerTest {
 
     mockGetPartition()
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1601,12 +1638,9 @@ class GroupMetadataManagerTest {
     // expire only one of the offsets
     time.sleep(2)
 
-    EasyMock.reset(partition)
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
-
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
     assertEquals(Some(group), groupMetadataManager.getGroup(groupId))
@@ -1617,7 +1651,16 @@ class GroupMetadataManagerTest {
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition1).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition2).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    verify(replicaManager, times(2)).getMagic(any())
   }
 
   @Test
@@ -1632,20 +1675,15 @@ class GroupMetadataManagerTest {
     group.generationId = 5
 
     // expect the group metadata tombstone
-    EasyMock.reset(partition)
-    val recordsCapture: Capture[MemoryRecords] = EasyMock.newCapture()
+    val recordsCapture: ArgumentCaptor[MemoryRecords] = ArgumentCaptor.forClass(classOf[MemoryRecords])
 
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
     mockGetPartition()
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.capture(recordsCapture),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(replicaManager, partition)
-
+    when(partition.appendRecordsToLeader(recordsCapture.capture(),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
-    assertTrue(recordsCapture.hasCaptured)
-
     val records = recordsCapture.getValue.records.asScala.toList
     recordsCapture.getValue.batches.forEach { batch =>
       assertEquals(RecordBatch.CURRENT_MAGIC_VALUE, batch.magic)
@@ -1680,20 +1718,15 @@ class GroupMetadataManagerTest {
     group.generationId = 5
 
     // expect the group metadata tombstone
-    EasyMock.reset(partition)
-    val recordsCapture: Capture[MemoryRecords] = EasyMock.newCapture()
+    val recordsCapture: ArgumentCaptor[MemoryRecords] = ArgumentCaptor.forClass(classOf[MemoryRecords])
 
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
     mockGetPartition()
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.capture(recordsCapture),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(replicaManager, partition)
-
+    when(partition.appendRecordsToLeader(recordsCapture.capture(),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
-    assertTrue(recordsCapture.hasCaptured)
-
     val records = recordsCapture.getValue.records.asScala.toList
     recordsCapture.getValue.batches.forEach { batch =>
       assertEquals(RecordBatch.CURRENT_MAGIC_VALUE, batch.magic)
@@ -1740,8 +1773,6 @@ class GroupMetadataManagerTest {
 
     mockGetPartition()
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1757,18 +1788,13 @@ class GroupMetadataManagerTest {
     time.sleep(4)
 
     // expect the offset tombstone
-    EasyMock.reset(partition)
-    val recordsCapture: Capture[MemoryRecords] = EasyMock.newCapture()
-
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.capture(recordsCapture),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
+    val recordsCapture: ArgumentCaptor[MemoryRecords] = ArgumentCaptor.forClass(classOf[MemoryRecords])
 
+    when(partition.appendRecordsToLeader(recordsCapture.capture(),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
-    assertTrue(recordsCapture.hasCaptured)
-
     // verify the tombstones are correct and only for the expired offsets
     val records = recordsCapture.getValue.records.asScala.toList
     assertEquals(2, records.size)
@@ -1786,7 +1812,7 @@ class GroupMetadataManagerTest {
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition1).map(_.offset))
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition2).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).onlinePartition(groupTopicPartition)
   }
 
   @Test
@@ -1825,8 +1851,6 @@ class GroupMetadataManagerTest {
 
     mockGetPartition()
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1854,18 +1878,15 @@ class GroupMetadataManagerTest {
     assertEquals(Some(offset), cachedOffsets.get(topicPartition2).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition3).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).onlinePartition(groupTopicPartition)
 
     group.transitionTo(PreparingRebalance)
     group.transitionTo(Empty)
 
     // expect the offset tombstone
-    EasyMock.reset(partition)
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
-
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
     // group is empty now, only one offset should expire
@@ -1879,17 +1900,14 @@ class GroupMetadataManagerTest {
     assertEquals(Some(offset), cachedOffsets.get(topicPartition2).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition3).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, times(2)).onlinePartition(groupTopicPartition)
 
     time.sleep(2)
 
     // expect the offset tombstone
-    EasyMock.reset(partition)
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
-
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
     // one more offset should expire
@@ -1903,7 +1921,7 @@ class GroupMetadataManagerTest {
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition2).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition3).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, times(3)).onlinePartition(groupTopicPartition)
 
     // advance time to just before the offset of last partition is to be expired, no offset should expire
     time.sleep(group.currentStateTimestamp.get + defaultOffsetRetentionMs - time.milliseconds() - 1)
@@ -1921,18 +1939,15 @@ class GroupMetadataManagerTest {
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition2).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition3).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, times(4)).onlinePartition(groupTopicPartition)
 
     // advance time enough for that last offset to expire
     time.sleep(2)
 
     // expect the offset tombstone
-    EasyMock.reset(partition)
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
-
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
     // group and all its offsets should be gone now
@@ -1946,7 +1961,7 @@ class GroupMetadataManagerTest {
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition2).map(_.offset))
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition3).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, times(5)).onlinePartition(groupTopicPartition)
 
     assert(group.is(Dead))
   }
@@ -1973,8 +1988,6 @@ class GroupMetadataManagerTest {
 
     mockGetPartition()
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -1999,18 +2012,15 @@ class GroupMetadataManagerTest {
     var cachedOffsets = groupMetadataManager.getOffsets(groupId, defaultRequireStable, Some(Seq(topicPartition1)))
     assertEquals(Some(offset), cachedOffsets.get(topicPartition1).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).onlinePartition(groupTopicPartition)
 
     // advance time to enough for offsets to expire
     time.sleep(2)
 
     // expect the offset tombstone
-    EasyMock.reset(partition)
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.replay(partition)
-
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
     groupMetadataManager.cleanupGroupMetadata()
 
     // group and all its offsets should be gone now
@@ -2020,7 +2030,7 @@ class GroupMetadataManagerTest {
     cachedOffsets = groupMetadataManager.getOffsets(groupId, defaultRequireStable, Some(Seq(topicPartition1)))
     assertEquals(Some(OffsetFetchResponse.INVALID_OFFSET), cachedOffsets.get(topicPartition1).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, times(2)).onlinePartition(groupTopicPartition)
 
     assert(group.is(Dead))
   }
@@ -2081,8 +2091,6 @@ class GroupMetadataManagerTest {
 
     mockGetPartition()
     expectAppendMessage(Errors.NONE)
-    EasyMock.replay(replicaManager)
-
     var commitErrors: Option[immutable.Map[TopicPartition, Errors]] = None
     def callback(errors: immutable.Map[TopicPartition, Errors]): Unit = {
       commitErrors = Some(errors)
@@ -2115,7 +2123,7 @@ class GroupMetadataManagerTest {
     assertEquals(Some(offset), cachedOffsets.get(topic2Partition0).map(_.offset))
     assertEquals(Some(offset), cachedOffsets.get(topic2Partition1).map(_.offset))
 
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).onlinePartition(groupTopicPartition)
 
     group.transitionTo(PreparingRebalance)
 
@@ -2132,17 +2140,16 @@ class GroupMetadataManagerTest {
     group.transitionTo(Stable)
 
     // expect the offset tombstone
-    EasyMock.expect(partition.appendRecordsToLeader(EasyMock.anyObject(classOf[MemoryRecords]),
-      origin = EasyMock.eq(AppendOrigin.Coordinator), requiredAcks = EasyMock.anyInt(),
-      EasyMock.anyObject())).andReturn(LogAppendInfo.UnknownLogAppendInfo)
-    EasyMock.expectLastCall().times(1)
-
-    EasyMock.replay(partition)
+    when(partition.appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())).thenReturn(LogAppendInfo.UnknownLogAppendInfo)
 
     groupMetadataManager.cleanupGroupMetadata()
 
-    EasyMock.verify(partition)
-    EasyMock.verify(replicaManager)
+    verify(partition).appendRecordsToLeader(any[MemoryRecords],
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator), requiredAcks = anyInt(),
+      any())
+    verify(replicaManager, times(2)).onlinePartition(groupTopicPartition)
 
     assertEquals(Some(group), groupMetadataManager.getGroup(groupId))
     assert(group.is(Stable))
@@ -2183,8 +2190,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -2224,8 +2229,6 @@ class GroupMetadataManagerTest {
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
 
-    EasyMock.replay(replicaManager)
-
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     val group = groupMetadataManager.getGroup(groupId).getOrElse(throw new AssertionError("Group was not loaded into the cache"))
@@ -2354,30 +2357,24 @@ class GroupMetadataManagerTest {
       (offsetCommitRecords ++ Seq(groupMetadataRecord)).toArray: _*)
 
     // Prepend empty control batch to valid records
-    val mockBatch: MutableRecordBatch = EasyMock.createMock(classOf[MutableRecordBatch])
-    EasyMock.expect(mockBatch.iterator).andReturn(Collections.emptyIterator[Record])
-    EasyMock.expect(mockBatch.isControlBatch).andReturn(true)
-    EasyMock.expect(mockBatch.isTransactional).andReturn(true)
-    EasyMock.expect(mockBatch.nextOffset).andReturn(16L)
-    EasyMock.replay(mockBatch)
-    val mockRecords: MemoryRecords = EasyMock.createMock(classOf[MemoryRecords])
-    EasyMock.expect(mockRecords.batches).andReturn((Iterable[MutableRecordBatch](mockBatch) ++ records.batches.asScala).asJava).anyTimes()
-    EasyMock.expect(mockRecords.records).andReturn(records.records()).anyTimes()
-    EasyMock.expect(mockRecords.sizeInBytes()).andReturn(DefaultRecordBatch.RECORD_BATCH_OVERHEAD + records.sizeInBytes()).anyTimes()
-    EasyMock.replay(mockRecords)
-
-    val logMock: UnifiedLog = EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(logMock.logStartOffset).andReturn(startOffset).anyTimes()
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true)))
-      .andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), mockRecords))
-    EasyMock.expect(replicaManager.getLog(groupMetadataTopicPartition)).andStubReturn(Some(logMock))
-    EasyMock.expect(replicaManager.getLogEndOffset(groupMetadataTopicPartition)).andStubReturn(Some(18))
-    EasyMock.replay(logMock)
-    EasyMock.replay(replicaManager)
-
+    val mockBatch: MutableRecordBatch = mock(classOf[MutableRecordBatch])
+    when(mockBatch.iterator).thenReturn(Collections.emptyIterator[Record])
+    when(mockBatch.isControlBatch).thenReturn(true)
+    when(mockBatch.isTransactional).thenReturn(true)
+    when(mockBatch.nextOffset).thenReturn(16L)
+    val mockRecords: MemoryRecords = mock(classOf[MemoryRecords])
+    when(mockRecords.batches).thenReturn((Iterable[MutableRecordBatch](mockBatch) ++ records.batches.asScala).asJava)
+    when(mockRecords.records).thenReturn(records.records())
+    when(mockRecords.sizeInBytes()).thenReturn(DefaultRecordBatch.RECORD_BATCH_OVERHEAD + records.sizeInBytes())
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), mockRecords))
+    when(replicaManager.getLog(groupMetadataTopicPartition)).thenReturn(Some(logMock))
+    when(replicaManager.getLogEndOffset(groupMetadataTopicPartition)).thenReturn(Some[Long](18))
     groupMetadataManager.loadGroupsAndOffsets(groupMetadataTopicPartition, groupEpoch, _ => (), 0L)
 
     // Empty control batch should not have caused the load to fail
@@ -2449,41 +2446,39 @@ class GroupMetadataManagerTest {
     assertEquals(Some("<DELETE>"), valueStringOpt)
   }
 
-  private def appendAndCaptureCallback(): Capture[Map[TopicPartition, PartitionResponse] => Unit] = {
-    val capturedArgument: Capture[Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    )
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+  private def verifyAppendAndCaptureCallback(): ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = {
+    val capturedArgument: ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, PartitionResponse] => Unit])
+    verify(replicaManager).appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
     capturedArgument
   }
 
-  private def expectAppendMessage(error: Errors): Capture[Map[TopicPartition, MemoryRecords]] = {
-    val capturedCallback: Capture[Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-    val capturedRecords: Capture[Map[TopicPartition, MemoryRecords]] = EasyMock.newCapture()
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.capture(capturedRecords),
-      EasyMock.capture(capturedCallback),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = capturedCallback.getValue.apply(
+  private def expectAppendMessage(error: Errors): ArgumentCaptor[Map[TopicPartition, MemoryRecords]] = {
+    val capturedCallback: ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, PartitionResponse] => Unit])
+    val capturedRecords: ArgumentCaptor[Map[TopicPartition, MemoryRecords]] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, MemoryRecords]])
+    when(replicaManager.appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      capturedRecords.capture(),
+      capturedCallback.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    ).thenAnswer(_ => {
+      capturedCallback.getValue.apply(
         Map(groupTopicPartition ->
           new PartitionResponse(error, 0L, RecordBatch.NO_TIMESTAMP, 0L)
         )
       )})
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject())).andStubReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
+    when(replicaManager.getMagic(any())).thenReturn(Some(RecordBatch.CURRENT_MAGIC_VALUE))
     capturedRecords
   }
 
@@ -2512,11 +2507,10 @@ class GroupMetadataManagerTest {
   private def expectGroupMetadataLoad(groupMetadataTopicPartition: TopicPartition,
                                       startOffset: Long,
                                       records: MemoryRecords): Unit = {
-    val logMock: UnifiedLog =  EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(replicaManager.getLog(groupMetadataTopicPartition)).andStubReturn(Some(logMock))
+    val logMock: UnifiedLog =  mock(classOf[UnifiedLog])
+    when(replicaManager.getLog(groupMetadataTopicPartition)).thenReturn(Some(logMock))
     val endOffset = expectGroupMetadataLoad(logMock, startOffset, records)
-    EasyMock.expect(replicaManager.getLogEndOffset(groupMetadataTopicPartition)).andStubReturn(Some(endOffset))
-    EasyMock.replay(logMock)
+    when(replicaManager.getLogEndOffset(groupMetadataTopicPartition)).thenReturn(Some(endOffset))
   }
 
   /**
@@ -2528,29 +2522,23 @@ class GroupMetadataManagerTest {
                                       startOffset: Long,
                                       records: MemoryRecords): Long = {
     val endOffset = startOffset + records.records.asScala.size
-    val fileRecordsMock: FileRecords = EasyMock.mock(classOf[FileRecords])
-
-    EasyMock.expect(logMock.logStartOffset).andStubReturn(startOffset)
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true)))
-      .andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
-
-    EasyMock.expect(fileRecordsMock.sizeInBytes()).andStubReturn(records.sizeInBytes)
-
-    val bufferCapture = EasyMock.newCapture[ByteBuffer]
-    fileRecordsMock.readInto(EasyMock.capture(bufferCapture), EasyMock.anyInt())
-    EasyMock.expectLastCall().andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = {
-        val buffer = bufferCapture.getValue
-        buffer.put(records.buffer.duplicate)
-        buffer.flip()
-      }
+    val fileRecordsMock: FileRecords = mock(classOf[FileRecords])
+
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
+
+    when(fileRecordsMock.sizeInBytes()).thenReturn(records.sizeInBytes)
+
+    val bufferCapture: ArgumentCaptor[ByteBuffer] = ArgumentCaptor.forClass(classOf[ByteBuffer])
+    when(fileRecordsMock.readInto(bufferCapture.capture(), anyInt())).thenAnswer(_ => {
+      val buffer = bufferCapture.getValue
+      buffer.put(records.buffer.duplicate)
+      buffer.flip()
     })
-
-    EasyMock.replay(fileRecordsMock)
-
     endOffset
   }
 
@@ -2574,8 +2562,8 @@ class GroupMetadataManagerTest {
   }
 
   private def mockGetPartition(): Unit = {
-    EasyMock.expect(replicaManager.getPartition(groupTopicPartition)).andStubReturn(HostedPartition.Online(partition))
-    EasyMock.expect(replicaManager.onlinePartition(groupTopicPartition)).andStubReturn(Some(partition))
+    when(replicaManager.getPartition(groupTopicPartition)).thenReturn(HostedPartition.Online(partition))
+    when(replicaManager.onlinePartition(groupTopicPartition)).thenReturn(Some(partition))
   }
 
   private def getGauge(manager: GroupMetadataManager, name: String): Gauge[Int]  = {
@@ -2638,8 +2626,6 @@ class GroupMetadataManagerTest {
       (offsetCommitRecords ++ Seq(groupMetadataRecord)).toArray: _*)
 
     expectGroupMetadataLoad(groupMetadataTopicPartition, startOffset, records)
-    EasyMock.replay(replicaManager)
-
     // When passed a specific start offset, assert that the measured values are in excess of that.
     val now = time.milliseconds()
     val diff = 1000
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
index 9232bf0..eefe61d 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/ProducerIdManagerTest.scala
@@ -23,18 +23,20 @@ import org.apache.kafka.common.message.AllocateProducerIdsResponseData
 import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.requests.AllocateProducerIdsResponse
 import org.apache.kafka.server.common.ProducerIdsBlock
-import org.easymock.{Capture, EasyMock}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.{EnumSource, ValueSource}
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.{any, anyString}
+import org.mockito.Mockito.{mock, when}
 
 import java.util.stream.IntStream
 
 class ProducerIdManagerTest {
 
-  var brokerToController: BrokerToControllerChannelManager = EasyMock.niceMock(classOf[BrokerToControllerChannelManager])
-  val zkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
+  var brokerToController: BrokerToControllerChannelManager = mock(classOf[BrokerToControllerChannelManager])
+  val zkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
 
   // Mutable test implementation that lets us easily set the idStart and error
   class MockProducerIdManager(val brokerId: Int, var idStart: Long, val idLen: Int, var error: Errors = Errors.NONE)
@@ -56,23 +58,21 @@ class ProducerIdManagerTest {
   def testGetProducerIdZk(): Unit = {
     var zkVersion: Option[Int] = None
     var data: Array[Byte] = null
-    EasyMock.expect(zkClient.getDataAndVersion(EasyMock.anyString)).andAnswer(() =>
-      zkVersion.map(Some(data) -> _).getOrElse(None, 0)).anyTimes()
-
-    val capturedVersion: Capture[Int] = EasyMock.newCapture()
-    val capturedData: Capture[Array[Byte]] = EasyMock.newCapture()
-    EasyMock.expect(zkClient.conditionalUpdatePath(EasyMock.anyString(),
-      EasyMock.capture(capturedData),
-      EasyMock.capture(capturedVersion),
-      EasyMock.anyObject[Option[(KafkaZkClient, String, Array[Byte]) => (Boolean, Int)]])
-    ).andAnswer(() => {
+    when(zkClient.getDataAndVersion(anyString)).thenAnswer(_ =>
+      zkVersion.map(Some(data) -> _).getOrElse(None, 0))
+
+    val capturedVersion: ArgumentCaptor[Int] = ArgumentCaptor.forClass(classOf[Int])
+    val capturedData: ArgumentCaptor[Array[Byte]] = ArgumentCaptor.forClass(classOf[Array[Byte]])
+    when(zkClient.conditionalUpdatePath(anyString(),
+      capturedData.capture(),
+      capturedVersion.capture(),
+      any[Option[(KafkaZkClient, String, Array[Byte]) => (Boolean, Int)]])
+    ).thenAnswer(_ => {
       val newZkVersion = capturedVersion.getValue + 1
       zkVersion = Some(newZkVersion)
       data = capturedData.getValue
       (true, newZkVersion)
-    }).anyTimes()
-
-    EasyMock.replay(zkClient)
+    })
 
     val manager1 = new ZkProducerIdManager(0, zkClient)
     val manager2 = new ZkProducerIdManager(1, zkClient)
@@ -91,18 +91,15 @@ class ProducerIdManagerTest {
 
     assertEquals(pid2 + ProducerIdsBlock.PRODUCER_ID_BLOCK_SIZE, manager1.generateProducerId())
     assertEquals(pid2 + ProducerIdsBlock.PRODUCER_ID_BLOCK_SIZE * 2, manager2.generateProducerId())
-
-    EasyMock.reset(zkClient)
   }
 
   @Test
   def testExceedProducerIdLimitZk(): Unit = {
-    EasyMock.expect(zkClient.getDataAndVersion(EasyMock.anyString)).andAnswer(() => {
+    when(zkClient.getDataAndVersion(anyString)).thenAnswer(_ => {
       val json = ProducerIdBlockZNode.generateProducerIdBlockJson(
         new ProducerIdsBlock(0, Long.MaxValue - ProducerIdsBlock.PRODUCER_ID_BLOCK_SIZE, ProducerIdsBlock.PRODUCER_ID_BLOCK_SIZE))
       (Some(json), 0)
-    }).anyTimes()
-    EasyMock.replay(zkClient)
+    })
     assertThrows(classOf[KafkaException], () => new ZkProducerIdManager(0, zkClient))
   }
 
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorConcurrencyTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorConcurrencyTest.scala
index 11451bf..9667d2f 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorConcurrencyTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorConcurrencyTest.scala
@@ -23,20 +23,23 @@ import java.util.concurrent.atomic.AtomicBoolean
 import kafka.coordinator.AbstractCoordinatorConcurrencyTest
 import kafka.coordinator.AbstractCoordinatorConcurrencyTest._
 import kafka.coordinator.transaction.TransactionCoordinatorConcurrencyTest._
-import kafka.log.{UnifiedLog, LogConfig}
+import kafka.log.{LogConfig, UnifiedLog}
 import kafka.server.{FetchDataInfo, FetchLogEnd, KafkaConfig, LogOffsetMetadata, MetadataCache, RequestLocal}
 import kafka.utils.{Pool, TestUtils}
 import org.apache.kafka.clients.{ClientResponse, NetworkClient}
 import org.apache.kafka.common.internals.Topic.TRANSACTION_STATE_TOPIC_NAME
 import org.apache.kafka.common.metrics.Metrics
+import org.apache.kafka.common.network.ListenerName
 import org.apache.kafka.common.protocol.{ApiKeys, Errors}
 import org.apache.kafka.common.record.{CompressionType, FileRecords, MemoryRecords, RecordBatch, SimpleRecord}
 import org.apache.kafka.common.requests._
 import org.apache.kafka.common.utils.{LogContext, MockTime, ProducerIdAndEpoch}
 import org.apache.kafka.common.{Node, TopicPartition}
-import org.easymock.{EasyMock, IAnswer}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.ArgumentMatchers.{any, anyInt, anyString}
+import org.mockito.Mockito.{mock, when}
 
 import scala.jdk.CollectionConverters._
 import scala.collection.{Map, mutable}
@@ -60,17 +63,15 @@ class TransactionCoordinatorConcurrencyTest extends AbstractCoordinatorConcurren
   private val txnRecordsByPartition: Map[Int, mutable.ArrayBuffer[SimpleRecord]] =
     (0 until numPartitions).map { i => (i, mutable.ArrayBuffer[SimpleRecord]()) }.toMap
 
-  val producerId = 11
+  val producerId: Long = 11
   private var bumpProducerId = false
 
   @BeforeEach
   override def setUp(): Unit = {
     super.setUp()
 
-    EasyMock.expect(zkClient.getTopicPartitionCount(TRANSACTION_STATE_TOPIC_NAME))
-      .andReturn(Some(numPartitions))
-      .anyTimes()
-    EasyMock.replay(zkClient)
+    when(zkClient.getTopicPartitionCount(TRANSACTION_STATE_TOPIC_NAME))
+      .thenReturn(Some(numPartitions))
 
     txnStateManager = new TransactionStateManager(0, scheduler, replicaManager, txnConfig, time,
       new Metrics())
@@ -79,18 +80,17 @@ class TransactionCoordinatorConcurrencyTest extends AbstractCoordinatorConcurren
     for (i <- 0 until numPartitions)
       txnStateManager.addLoadedTransactionsToCache(i, coordinatorEpoch, new Pool[String, TransactionMetadata]())
 
-    val pidGenerator: ProducerIdManager = EasyMock.createNiceMock(classOf[ProducerIdManager])
-    EasyMock.expect(pidGenerator.generateProducerId())
-      .andAnswer(() => if (bumpProducerId) producerId + 1 else producerId)
-      .anyTimes()
+    val pidGenerator: ProducerIdManager = mock(classOf[ProducerIdManager])
+    when(pidGenerator.generateProducerId())
+      .thenAnswer(_ => if (bumpProducerId) producerId + 1 else producerId)
     val brokerNode = new Node(0, "host", 10)
-    val metadataCache: MetadataCache = EasyMock.createNiceMock(classOf[MetadataCache])
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.anyString(),
-      EasyMock.anyInt(),
-      EasyMock.anyObject())
-    ).andReturn(Some(brokerNode)).anyTimes()
-    val networkClient: NetworkClient = EasyMock.createNiceMock(classOf[NetworkClient])
+    val metadataCache: MetadataCache = mock(classOf[MetadataCache])
+    when(metadataCache.getPartitionLeaderEndpoint(
+      anyString,
+      anyInt,
+      any[ListenerName])
+    ).thenReturn(Some(brokerNode))
+    val networkClient: NetworkClient = mock(classOf[NetworkClient])
     txnMarkerChannelManager = new TransactionMarkerChannelManager(
       KafkaConfig.fromProps(serverProps),
       metadataCache,
@@ -106,15 +106,11 @@ class TransactionCoordinatorConcurrencyTest extends AbstractCoordinatorConcurren
       txnMarkerChannelManager,
       time,
       new LogContext)
-    EasyMock.replay(pidGenerator)
-    EasyMock.replay(metadataCache)
-    EasyMock.replay(networkClient)
   }
 
   @AfterEach
   override def tearDown(): Unit = {
     try {
-      EasyMock.reset(zkClient, replicaManager)
       transactionCoordinator.shutdown()
     } finally {
       super.tearDown()
@@ -458,36 +454,31 @@ class TransactionCoordinatorConcurrencyTest extends AbstractCoordinatorConcurren
   }
 
   private def prepareTxnLog(partitionId: Int): Unit = {
-    val logMock: UnifiedLog =  EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(logMock.config).andStubReturn(new LogConfig(Collections.emptyMap()))
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    when(logMock.config).thenReturn(new LogConfig(Collections.emptyMap()))
 
-    val fileRecordsMock: FileRecords = EasyMock.mock(classOf[FileRecords])
+    val fileRecordsMock: FileRecords = mock(classOf[FileRecords])
 
     val topicPartition = new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, partitionId)
     val startOffset = replicaManager.getLogEndOffset(topicPartition).getOrElse(20L)
     val records = MemoryRecords.withRecords(startOffset, CompressionType.NONE, txnRecordsByPartition(partitionId).toArray: _*)
     val endOffset = startOffset + records.records.asScala.size
 
-    EasyMock.expect(logMock.logStartOffset).andStubReturn(startOffset)
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true)))
-      .andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
-
-    EasyMock.expect(fileRecordsMock.sizeInBytes()).andStubReturn(records.sizeInBytes)
-
-    val bufferCapture = EasyMock.newCapture[ByteBuffer]
-    fileRecordsMock.readInto(EasyMock.capture(bufferCapture), EasyMock.anyInt())
-    EasyMock.expectLastCall().andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = {
-        val buffer = bufferCapture.getValue
-        buffer.put(records.buffer.duplicate)
-        buffer.flip()
-      }
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt,
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
+
+    when(fileRecordsMock.sizeInBytes()).thenReturn(records.sizeInBytes)
+    val bufferCaptor: ArgumentCaptor[ByteBuffer] = ArgumentCaptor.forClass(classOf[ByteBuffer])
+    when(fileRecordsMock.readInto(bufferCaptor.capture(), anyInt)).thenAnswer(_ => {
+      val buffer = bufferCaptor.getValue
+      buffer.put(records.buffer.duplicate)
+      buffer.flip()
     })
 
-    EasyMock.replay(logMock, fileRecordsMock)
     synchronized {
       replicaManager.updateLog(topicPartition, logMock, endOffset)
     }
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorTest.scala
index f6583ef..3c416f1 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionCoordinatorTest.scala
@@ -22,9 +22,11 @@ import org.apache.kafka.common.protocol.Errors
 import org.apache.kafka.common.record.RecordBatch
 import org.apache.kafka.common.requests.TransactionResult
 import org.apache.kafka.common.utils.{LogContext, MockTime, ProducerIdAndEpoch}
-import org.easymock.{Capture, EasyMock}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.ArgumentMatchers.{any, anyInt}
+import org.mockito.Mockito.{mock, times, verify, when}
 
 import scala.collection.mutable
 import scala.jdk.CollectionConverters._
@@ -34,12 +36,12 @@ class TransactionCoordinatorTest {
   val time = new MockTime()
 
   var nextPid: Long = 0L
-  val pidGenerator: ProducerIdManager = EasyMock.createNiceMock(classOf[ProducerIdManager])
-  val transactionManager: TransactionStateManager = EasyMock.createNiceMock(classOf[TransactionStateManager])
-  val transactionMarkerChannelManager: TransactionMarkerChannelManager = EasyMock.createNiceMock(classOf[TransactionMarkerChannelManager])
-  val capturedTxn: Capture[TransactionMetadata] = EasyMock.newCapture()
-  val capturedErrorsCallback: Capture[Errors => Unit] = EasyMock.newCapture()
-  val capturedTxnTransitMetadata: Capture[TxnTransitMetadata] = EasyMock.newCapture()
+  val pidGenerator: ProducerIdManager = mock(classOf[ProducerIdManager])
+  val transactionManager: TransactionStateManager = mock(classOf[TransactionStateManager])
+  val transactionMarkerChannelManager: TransactionMarkerChannelManager = mock(classOf[TransactionMarkerChannelManager])
+  val capturedTxn: ArgumentCaptor[TransactionMetadata] = ArgumentCaptor.forClass(classOf[TransactionMetadata])
+  val capturedErrorsCallback: ArgumentCaptor[Errors => Unit] = ArgumentCaptor.forClass(classOf[Errors => Unit])
+  val capturedTxnTransitMetadata: ArgumentCaptor[TxnTransitMetadata] = ArgumentCaptor.forClass(classOf[TxnTransitMetadata])
   val brokerId = 0
   val coordinatorEpoch = 0
   private val transactionalId = "known"
@@ -63,23 +65,21 @@ class TransactionCoordinatorTest {
   var error: Errors = Errors.NONE
 
   private def mockPidGenerator(): Unit = {
-    EasyMock.expect(pidGenerator.generateProducerId()).andAnswer(() => {
+    when(pidGenerator.generateProducerId()).thenAnswer(_ => {
       nextPid += 1
       nextPid - 1
-    }).anyTimes()
+    })
   }
 
   private def initPidGenericMocks(transactionalId: String): Unit = {
     mockPidGenerator()
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-      .anyTimes()
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
   }
 
   @Test
   def shouldReturnInvalidRequestWhenTransactionalIdIsEmpty(): Unit = {
     mockPidGenerator()
-    EasyMock.replay(pidGenerator)
 
     coordinator.handleInitProducerId("", txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(-1L, -1, Errors.INVALID_REQUEST), result)
@@ -90,7 +90,6 @@ class TransactionCoordinatorTest {
   @Test
   def shouldAcceptInitPidAndReturnNextPidWhenTransactionalIdIsNull(): Unit = {
     mockPidGenerator()
-    EasyMock.replay(pidGenerator)
 
     coordinator.handleInitProducerId(null, txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(0L, 0, Errors.NONE), result)
@@ -102,25 +101,22 @@ class TransactionCoordinatorTest {
   def shouldInitPidWithEpochZeroForNewTransactionalId(): Unit = {
     initPidGenericMocks(transactionalId)
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(None))
-      .once()
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(None))
 
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.capture(capturedTxn)))
-      .andAnswer(() => {
-        assertTrue(capturedTxn.hasCaptured)
+    when(transactionManager.putTransactionStateIfNotExists(capturedTxn.capture()))
+      .thenAnswer(_ => {
         Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, capturedTxn.getValue))
-      }).once()
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.anyObject().asInstanceOf[TxnTransitMetadata],
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NONE)).anyTimes()
-    EasyMock.replay(pidGenerator, transactionManager)
+      })
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NONE))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(nextPid - 1, 0, Errors.NONE), result)
@@ -130,25 +126,22 @@ class TransactionCoordinatorTest {
   def shouldGenerateNewProducerIdIfNoStateAndProducerIdAndEpochProvided(): Unit = {
     initPidGenericMocks(transactionalId)
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(None))
-      .once()
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(None))
 
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.capture(capturedTxn)))
-      .andAnswer(() => {
-        assertTrue(capturedTxn.hasCaptured)
+    when(transactionManager.putTransactionStateIfNotExists(capturedTxn.capture()))
+      .thenAnswer(_ => {
         Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, capturedTxn.getValue))
-      }).once()
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.anyObject().asInstanceOf[TxnTransitMetadata],
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NONE)).anyTimes()
-    EasyMock.replay(pidGenerator, transactionManager)
+      })
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NONE))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId, producerEpoch)),
       initProducerIdMockCallback)
@@ -162,19 +155,17 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (Short.MaxValue - 1).toShort,
       (Short.MaxValue - 2).toShort, txnTimeoutMs, Empty, mutable.Set.empty, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.anyObject().asInstanceOf[TxnTransitMetadata],
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()
-    )).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NONE))
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
-    EasyMock.replay(pidGenerator, transactionManager)
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      capturedErrorsCallback.capture(),
+      any(),
+      any()
+    )).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NONE))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
     assertNotEquals(producerId, result.producerId)
@@ -184,25 +175,21 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithNotCoordinatorOnInitPidWhenNotCoordinator(): Unit = {
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-      .anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.NOT_COORDINATOR))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.NOT_COORDINATOR))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(-1, -1, Errors.NOT_COORDINATOR), result)
   }
 
   @Test
-  def shouldRespondWithCoordinatorLoadInProgressOnInitPidWhenCoordintorLoading(): Unit = {
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-      .anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
-    EasyMock.replay(transactionManager)
+  def shouldRespondWithCoordinatorLoadInProgressOnInitPidWhenCoordinatorLoading(): Unit = {
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(-1, -1, Errors.COORDINATOR_LOAD_IN_PROGRESS), result)
@@ -210,9 +197,8 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithInvalidPidMappingOnAddPartitionsToTransactionWhenTransactionalIdNotPresent(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(None))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(None))
 
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 1, partitions, errorsCallback)
     assertEquals(Errors.INVALID_PRODUCER_ID_MAPPING, error)
@@ -232,20 +218,17 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithNotCoordinatorOnAddPartitionsWhenNotCoordinator(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.NOT_COORDINATOR))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.NOT_COORDINATOR))
 
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 1, partitions, errorsCallback)
     assertEquals(Errors.NOT_COORDINATOR, error)
   }
 
   @Test
-  def shouldRespondWithCoordinatorLoadInProgressOnAddPartitionsWhenCoordintorLoading(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
-
-    EasyMock.replay(transactionManager)
+  def shouldRespondWithCoordinatorLoadInProgressOnAddPartitionsWhenCoordinatorLoading(): Unit = {
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
 
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 1, partitions, errorsCallback)
     assertEquals(Errors.COORDINATOR_LOAD_IN_PROGRESS, error)
@@ -262,24 +245,20 @@ class TransactionCoordinatorTest {
   }
 
   def validateConcurrentTransactions(state: TransactionState): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, 0, 0, 0, RecordBatch.NO_PRODUCER_EPOCH, 0, state, mutable.Set.empty, 0, 0)))))
 
-    EasyMock.replay(transactionManager)
-
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 0, partitions, errorsCallback)
     assertEquals(Errors.CONCURRENT_TRANSACTIONS, error)
   }
 
   @Test
   def shouldRespondWithProducerFencedOnAddPartitionsWhenEpochsAreDifferent(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, 0, 0, 10, 9, 0, PrepareCommit, mutable.Set.empty, 0, 0)))))
 
-    EasyMock.replay(transactionManager)
-
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 0, partitions, errorsCallback)
     assertEquals(Errors.PRODUCER_FENCED, error)
   }
@@ -308,163 +287,157 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort,
       txnTimeoutMs, previousState, mutable.Set.empty, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.anyObject().asInstanceOf[TxnTransitMetadata],
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()
-    ))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.handleAddPartitionsToTransaction(transactionalId, producerId, producerEpoch, partitions, errorsCallback)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      any(),
+      any(),
+      any()
+    )
   }
 
   @Test
   def shouldRespondWithErrorsNoneOnAddPartitionWhenNoErrorsAndPartitionsTheSame(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, 0, 0, 0, RecordBatch.NO_PRODUCER_EPOCH, 0, Empty, partitions, 0, 0)))))
 
-    EasyMock.replay(transactionManager)
-
     coordinator.handleAddPartitionsToTransaction(transactionalId, 0L, 0, partitions, errorsCallback)
     assertEquals(Errors.NONE, error)
-    EasyMock.verify(transactionManager)
-
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReplyWithInvalidPidMappingOnEndTxnWhenTxnIdDoesntExist(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(None))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(None))
 
     coordinator.handleEndTransaction(transactionalId, 0, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.INVALID_PRODUCER_ID_MAPPING, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReplyWithInvalidPidMappingOnEndTxnWhenPidDosentMatchMapped(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, 10, 10, 0, RecordBatch.NO_PRODUCER_EPOCH, 0, Ongoing, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
 
     coordinator.handleEndTransaction(transactionalId, 0, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.INVALID_PRODUCER_ID_MAPPING, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReplyWithProducerFencedOnEndTxnWhenEpochIsNotSameAsTransaction(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, Ongoing, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
 
     coordinator.handleEndTransaction(transactionalId, producerId, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.PRODUCER_FENCED, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnOkOnEndTxnWhenStatusIsCompleteCommitAndResultIsCommit(): Unit ={
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, CompleteCommit, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.NONE, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnOkOnEndTxnWhenStatusIsCompleteAbortAndResultIsAbort(): Unit ={
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, CompleteAbort, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.ABORT, errorsCallback)
     assertEquals(Errors.NONE, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnInvalidTxnRequestOnEndTxnRequestWhenStatusIsCompleteAbortAndResultIsNotAbort(): Unit = {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, CompleteAbort, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.INVALID_TXN_STATE, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnInvalidTxnRequestOnEndTxnRequestWhenStatusIsCompleteCommitAndResultIsNotCommit(): Unit = {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort,1, CompleteCommit, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.ABORT, errorsCallback)
     assertEquals(Errors.INVALID_TXN_STATE, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnConcurrentTxnRequestOnEndTxnRequestWhenStatusIsPrepareCommit(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, PrepareCommit, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, (producerEpoch - 1).toShort, 1, PrepareCommit, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.CONCURRENT_TRANSACTIONS, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldReturnInvalidTxnRequestOnEndTxnRequestWhenStatusIsPrepareAbort(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, new TransactionMetadata(transactionalId, producerId, producerId, 1, RecordBatch.NO_PRODUCER_EPOCH, 1, PrepareAbort, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, new TransactionMetadata(transactionalId, producerId, producerId, 1, RecordBatch.NO_PRODUCER_EPOCH, 1, PrepareAbort, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
 
     coordinator.handleEndTransaction(transactionalId, producerId, 1, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.INVALID_TXN_STATE, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
   def shouldAppendPrepareCommitToLogOnEndTxnWhenStatusIsOngoingAndResultIsCommit(): Unit = {
     mockPrepare(PrepareCommit)
 
-    EasyMock.replay(transactionManager)
-
     coordinator.handleEndTransaction(transactionalId, producerId, producerEpoch, TransactionResult.COMMIT, errorsCallback)
-
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any(),
+      any(),
+      any(),
+      any())
   }
 
   @Test
   def shouldAppendPrepareAbortToLogOnEndTxnWhenStatusIsOngoingAndResultIsAbort(): Unit = {
     mockPrepare(PrepareAbort)
 
-    EasyMock.replay(transactionManager)
-
     coordinator.handleEndTransaction(transactionalId, producerId, producerEpoch, TransactionResult.ABORT, errorsCallback)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any(),
+      any(),
+      any(),
+      any())
   }
 
   @Test
@@ -475,9 +448,8 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithInvalidRequestOnEndTxnWhenTransactionalIdIsEmpty(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.NOT_COORDINATOR))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.NOT_COORDINATOR))
 
     coordinator.handleEndTransaction("", 0, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.INVALID_REQUEST, error)
@@ -485,9 +457,8 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithNotCoordinatorOnEndTxnWhenIsNotCoordinatorForId(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.NOT_COORDINATOR))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.NOT_COORDINATOR))
 
     coordinator.handleEndTransaction(transactionalId, 0, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.NOT_COORDINATOR, error)
@@ -495,10 +466,8 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRespondWithCoordinatorLoadInProgressOnEndTxnWhenCoordinatorIsLoading(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
 
     coordinator.handleEndTransaction(transactionalId, 0, 0, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.COORDINATOR_LOAD_IN_PROGRESS, error)
@@ -517,14 +486,13 @@ class TransactionCoordinatorTest {
   }
 
   private def verifyEndTxnEpoch(metadataEpoch: Short, requestEpoch: Short): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch,
         new TransactionMetadata(transactionalId, producerId, producerId, metadataEpoch, 0, 1, CompleteCommit, collection.mutable.Set.empty[TopicPartition], 0, time.milliseconds())))))
-    EasyMock.replay(transactionManager)
 
     coordinator.handleEndTransaction(transactionalId, producerId, requestEpoch, TransactionResult.COMMIT, errorsCallback)
     assertEquals(Errors.PRODUCER_FENCED, error)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -557,35 +525,35 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       (producerEpoch - 1).toShort, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.anyObject[TransactionMetadata]()))
-      .andReturn(Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))
-      .anyTimes()
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .anyTimes()
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     val originalMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (producerEpoch + 1).toShort,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(originalMetadata.prepareAbortOrCommit(PrepareAbort, time.milliseconds())),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NONE))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NONE))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
 
     assertEquals(InitProducerIdResult(-1, -1, Errors.CONCURRENT_TRANSACTIONS), result)
-
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).validateTransactionTimeoutMs(anyInt())
+    verify(transactionManager, times(3)).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(originalMetadata.prepareAbortOrCommit(PrepareAbort, time.milliseconds())),
+      any(),
+      any(),
+      any())
   }
 
   @Test
@@ -593,31 +561,22 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       (producerEpoch - 1).toShort, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.anyObject[TransactionMetadata]()))
-      .andReturn(Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))
-      .anyTimes()
-
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .times(1)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
     val bumpedTxnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (producerEpoch + 2).toShort,
       (producerEpoch - 1).toShort, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, bumpedTxnMetadata))))
-      .times(1)
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, bumpedTxnMetadata))))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
 
     assertEquals(InitProducerIdResult(-1, -1, Errors.PRODUCER_FENCED), result)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).validateTransactionTimeoutMs(anyInt())
+    verify(transactionManager, times(2)).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -625,49 +584,38 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-      .anyTimes()
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.anyObject[TransactionMetadata]()))
-      .andReturn(Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))
-      .anyTimes()
+    when(transactionManager.putTransactionStateIfNotExists(any[TransactionMetadata]()))
+      .thenReturn(Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andAnswer(() => Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .anyTimes()
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenAnswer(_ => Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     val originalMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (producerEpoch + 1).toShort,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
     val txnTransitMetadata = originalMetadata.prepareAbortOrCommit(PrepareAbort, time.milliseconds())
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(txnTransitMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitMetadata),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
+      capturedErrorsCallback.getValue.apply(Errors.NOT_ENOUGH_REPLICAS)
+      txnMetadata.pendingState = None
+    }).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NOT_ENOUGH_REPLICAS)
       txnMetadata.pendingState = None
-    }).times(2)
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(txnTransitMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    }).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NONE)
 
       // For the successful call, execute the state transitions that would happen in appendTransactionToLog()
       txnMetadata.completeTransitionTo(txnTransitMetadata)
       txnMetadata.prepareComplete(time.milliseconds())
-    }).once()
-
-    EasyMock.replay(transactionManager)
+    })
 
     // For the first two calls, verify that the epoch was only bumped once
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
@@ -689,7 +637,15 @@ class TransactionCoordinatorTest {
     assertEquals((producerEpoch + 1).toShort, txnMetadata.producerEpoch)
     assertFalse(txnMetadata.hasFailedEpochFence)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager, times(3)).validateTransactionTimeoutMs(anyInt())
+    verify(transactionManager, times(9)).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager, times(3)).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitMetadata),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
   }
 
   @Test
@@ -698,28 +654,20 @@ class TransactionCoordinatorTest {
       (Short.MaxValue - 2).toShort, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
     assertTrue(txnMetadata.isProducerEpochExhausted)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
-
-    EasyMock.expect(transactionManager.putTransactionStateIfNotExists(EasyMock.anyObject[TransactionMetadata]()))
-      .andReturn(Right(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))
-      .anyTimes()
-
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .times(2)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
     val postFenceTxnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, Short.MaxValue,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, PrepareAbort, partitions, time.milliseconds(), time.milliseconds())
-
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, postFenceTxnMetadata))))
-      .once()
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(TxnTransitMetadata(
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, postFenceTxnMetadata))))
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(TxnTransitMetadata(
         producerId = producerId,
         lastProducerId = producerId,
         producerEpoch = Short.MaxValue,
@@ -729,18 +677,33 @@ class TransactionCoordinatorTest {
         topicPartitions = partitions.toSet,
         txnStartTimestamp = time.milliseconds(),
         txnLastUpdateTimestamp = time.milliseconds())),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NONE))
-
-    EasyMock.replay(transactionManager)
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NONE))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
     assertEquals(Short.MaxValue, txnMetadata.producerEpoch)
 
     assertEquals(InitProducerIdResult(-1, -1, Errors.CONCURRENT_TRANSACTIONS), result)
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).validateTransactionTimeoutMs(anyInt())
+    verify(transactionManager, times(3)).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(TxnTransitMetadata(
+        producerId = producerId,
+        lastProducerId = producerId,
+        producerEpoch = Short.MaxValue,
+        lastProducerEpoch = RecordBatch.NO_PRODUCER_EPOCH,
+        txnTimeoutMs = txnTimeoutMs,
+        txnState = PrepareAbort,
+        topicPartitions = partitions.toSet,
+        txnStartTimestamp = time.milliseconds(),
+        txnLastUpdateTimestamp = time.milliseconds())),
+      any(),
+      any(),
+      any())
   }
 
   @Test
@@ -750,11 +713,10 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, RecordBatch.NO_PRODUCER_ID, (producerEpoch + 1).toShort,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).once
-    EasyMock.replay(transactionManager)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     // Simulate producer trying to continue after new producer has already been initialized
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId, producerEpoch)),
@@ -768,11 +730,10 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId + 1, producerId, producerEpoch,
       (producerEpoch - 1).toShort, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).once
-    EasyMock.replay(transactionManager)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     // Simulate producer trying to continue after new producer has already been initialized
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId, producerEpoch)),
@@ -787,24 +748,22 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, 10,
       9, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).times(2)
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.anyObject().asInstanceOf[TxnTransitMetadata],
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      any[TxnTransitMetadata],
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NONE)
       txnMetadata.pendingState = None
-    }).times(2)
-
-    EasyMock.replay(pidGenerator, transactionManager)
+    })
 
     // Re-initialization should succeed and bump the producer epoch
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId, 10)),
@@ -824,27 +783,25 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, 10,
       9, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).times(2)
-
-    val capturedTxnTransitMetadata = Capture.newInstance[TxnTransitMetadata]
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.capture(capturedTxnTransitMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+
+    val capturedTxnTransitMetadata : ArgumentCaptor[TxnTransitMetadata] = ArgumentCaptor.forClass(classOf[TxnTransitMetadata])
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      capturedTxnTransitMetadata.capture(),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NONE)
       txnMetadata.pendingState = None
       txnMetadata.producerEpoch = capturedTxnTransitMetadata.getValue.producerEpoch
       txnMetadata.lastProducerEpoch = capturedTxnTransitMetadata.getValue.lastProducerEpoch
-    }).times(2)
-
-    EasyMock.replay(pidGenerator, transactionManager)
+    })
 
     // With producer epoch at 10, new producer calls InitProducerId and should get epoch 11
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, None, initProducerIdMockCallback)
@@ -862,32 +819,29 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (Short.MaxValue - 1).toShort,
       (Short.MaxValue - 2).toShort, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(pidGenerator.generateProducerId())
-      .andReturn(producerId + 1)
-      .anyTimes()
-
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).times(2)
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.capture(capturedTxnTransitMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(pidGenerator.generateProducerId())
+      .thenReturn(producerId + 1)
+
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      capturedTxnTransitMetadata.capture(),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NONE)
       txnMetadata.pendingState = None
       txnMetadata.producerId = capturedTxnTransitMetadata.getValue.producerId
       txnMetadata.lastProducerId = capturedTxnTransitMetadata.getValue.lastProducerId
       txnMetadata.producerEpoch = capturedTxnTransitMetadata.getValue.producerEpoch
       txnMetadata.lastProducerEpoch = capturedTxnTransitMetadata.getValue.lastProducerEpoch
-    }).once
-
-    EasyMock.replay(pidGenerator, transactionManager)
+    })
 
     // Bump epoch and cause producer ID to be rotated
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId,
@@ -906,32 +860,29 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (Short.MaxValue - 1).toShort,
       (Short.MaxValue - 2).toShort, txnTimeoutMs, Empty, partitions, time.milliseconds, time.milliseconds)
 
-    EasyMock.expect(pidGenerator.generateProducerId())
-      .andReturn(producerId + 1)
-      .anyTimes()
-
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata)))).times(2)
-
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.capture(capturedTxnTransitMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(pidGenerator.generateProducerId())
+      .thenReturn(producerId + 1)
+
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      capturedTxnTransitMetadata.capture(),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
       capturedErrorsCallback.getValue.apply(Errors.NONE)
       txnMetadata.pendingState = None
       txnMetadata.producerId = capturedTxnTransitMetadata.getValue.producerId
       txnMetadata.lastProducerId = capturedTxnTransitMetadata.getValue.lastProducerId
       txnMetadata.producerEpoch = capturedTxnTransitMetadata.getValue.producerEpoch
       txnMetadata.lastProducerEpoch = capturedTxnTransitMetadata.getValue.lastProducerEpoch
-    }).once
-
-    EasyMock.replay(pidGenerator, transactionManager)
+    })
 
     // Bump epoch and cause producer ID to be rotated
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId,
@@ -946,13 +897,9 @@ class TransactionCoordinatorTest {
 
   @Test
   def shouldRemoveTransactionsForPartitionOnEmigration(): Unit = {
-    EasyMock.expect(transactionManager.removeTransactionsForTxnTopicPartition(0, coordinatorEpoch))
-    EasyMock.expect(transactionMarkerChannelManager.removeMarkersForTxnTopicPartition(0))
-    EasyMock.replay(transactionManager, transactionMarkerChannelManager)
-
     coordinator.onResignation(0, Some(coordinatorEpoch))
-
-    EasyMock.verify(transactionManager, transactionMarkerChannelManager)
+    verify(transactionManager).removeTransactionsForTxnTopicPartition(0, coordinatorEpoch)
+    verify(transactionMarkerChannelManager).removeMarkersForTxnTopicPartition(0)
   }
 
   @Test
@@ -961,29 +908,33 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, now, now)
 
-    EasyMock.expect(transactionManager.timedOutTransactions())
-      .andReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .times(2)
+    when(transactionManager.timedOutTransactions())
+      .thenReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     val expectedTransition = TxnTransitMetadata(producerId, producerId, (producerEpoch + 1).toShort, RecordBatch.NO_PRODUCER_EPOCH,
       txnTimeoutMs, PrepareAbort, partitions.toSet, now, now + TransactionStateManager.DefaultAbortTimedOutTransactionsIntervalMs)
 
-    EasyMock.expect(transactionManager.appendTransactionToLog(EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(expectedTransition),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {}).once()
-
-    EasyMock.replay(transactionManager, transactionMarkerChannelManager)
+    when(transactionManager.appendTransactionToLog(ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {})
 
     coordinator.startup(() => transactionStatePartitionCount, false)
     time.sleep(TransactionStateManager.DefaultAbortTimedOutTransactionsIntervalMs)
     scheduler.tick()
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).timedOutTransactions()
+    verify(transactionManager, times(2)).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
   }
 
   @Test
@@ -992,24 +943,23 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, now, now)
 
-    EasyMock.expect(transactionManager.timedOutTransactions())
-      .andReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+    when(transactionManager.timedOutTransactions())
+      .thenReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     val bumpedTxnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, (producerEpoch + 2).toShort,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, now, now)
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, bumpedTxnMetadata))))
-
-    EasyMock.replay(transactionManager, transactionMarkerChannelManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, bumpedTxnMetadata))))
 
     def checkOnEndTransactionComplete(txnIdAndPidEpoch: TransactionalIdAndProducerIdEpoch)(error: Errors): Unit = {
       assertEquals(Errors.PRODUCER_FENCED, error)
     }
     coordinator.abortTimedOutTransactions(checkOnEndTransactionComplete)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).timedOutTransactions()
+    verify(transactionManager, times(2)).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -1018,17 +968,16 @@ class TransactionCoordinatorTest {
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
     metadata.prepareAbortOrCommit(PrepareCommit, time.milliseconds())
 
-    EasyMock.expect(transactionManager.timedOutTransactions())
-      .andReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata)))).once()
-
-    EasyMock.replay(transactionManager, transactionMarkerChannelManager)
+    when(transactionManager.timedOutTransactions())
+      .thenReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata))))
 
     coordinator.startup(() => transactionStatePartitionCount, false)
     time.sleep(TransactionStateManager.DefaultAbortTimedOutTransactionsIntervalMs)
     scheduler.tick()
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).timedOutTransactions()
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -1037,37 +986,40 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, now, now)
 
-
-    EasyMock.expect(transactionManager.timedOutTransactions())
-      .andReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .times(2)
+    when(transactionManager.timedOutTransactions())
+      .thenReturn(List(TransactionalIdAndProducerIdEpoch(transactionalId, producerId, producerEpoch)))
 
     val txnMetadataAfterAppendFailure = new TransactionMetadata(transactionalId, producerId, producerId, (producerEpoch + 1).toShort,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, now, now)
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andAnswer(() => Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadataAfterAppendFailure))))
-      .once
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadataAfterAppendFailure))))
 
     val bumpedEpoch = (producerEpoch + 1).toShort
     val expectedTransition = TxnTransitMetadata(producerId, producerId, bumpedEpoch, RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs,
       PrepareAbort, partitions.toSet, now, now + TransactionStateManager.DefaultAbortTimedOutTransactionsIntervalMs)
 
-    EasyMock.expect(transactionManager.appendTransactionToLog(EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(expectedTransition),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.NOT_ENOUGH_REPLICAS)).once()
-
-    EasyMock.replay(transactionManager, transactionMarkerChannelManager)
+    when(transactionManager.appendTransactionToLog(ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.NOT_ENOUGH_REPLICAS))
 
     coordinator.startup(() => transactionStatePartitionCount, false)
     time.sleep(TransactionStateManager.DefaultAbortTimedOutTransactionsIntervalMs)
     scheduler.tick()
-    EasyMock.verify(transactionManager)
+
+    verify(transactionManager).timedOutTransactions()
+    verify(transactionManager, times(3)).getTransactionState(ArgumentMatchers.eq(transactionalId))
+    verify(transactionManager).appendTransactionToLog(ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
 
     assertEquals((producerEpoch + 1).toShort, txnMetadataAfterAppendFailure.producerEpoch)
     assertTrue(txnMetadataAfterAppendFailure.hasFailedEpochFence)
@@ -1079,18 +1031,17 @@ class TransactionCoordinatorTest {
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
     txnMetadata.prepareAbortOrCommit(PrepareCommit, time.milliseconds())
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.handleInitProducerId(transactionalId, txnTimeoutMs, Some(new ProducerIdAndEpoch(producerId, 10)),
       initProducerIdMockCallback)
     assertEquals(InitProducerIdResult(RecordBatch.NO_PRODUCER_ID, RecordBatch.NO_PRODUCER_EPOCH, Errors.CONCURRENT_TRANSACTIONS), result)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).validateTransactionTimeoutMs(anyInt())
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -1108,9 +1059,8 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Dead, mutable.Set.empty, time.milliseconds(),
       time.milliseconds())
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     val result = coordinator.handleDescribeTransactions(transactionalId)
     assertEquals(transactionalId, result.transactionalId)
@@ -1119,17 +1069,15 @@ class TransactionCoordinatorTest {
 
   @Test
   def testDescribeTransactionsWhileCoordinatorLoading(): Unit = {
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
 
     coordinator.startup(() => transactionStatePartitionCount, enableTransactionalIdExpiration = false)
     val result = coordinator.handleDescribeTransactions(transactionalId)
     assertEquals(transactionalId, result.transactionalId)
     assertEquals(Errors.COORDINATOR_LOAD_IN_PROGRESS, Errors.forCode(result.errorCode))
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   @Test
@@ -1137,10 +1085,8 @@ class TransactionCoordinatorTest {
     val txnMetadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch,
       RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, Ongoing, partitions, time.milliseconds(), time.milliseconds())
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
 
     coordinator.startup(() => transactionStatePartitionCount, enableTransactionalIdExpiration = false)
     val result = coordinator.handleDescribeTransactions(transactionalId)
@@ -1156,18 +1102,16 @@ class TransactionCoordinatorTest {
     }.toSet
     assertEquals(partitions, addedPartitions)
 
-    EasyMock.verify(transactionManager)
+    verify(transactionManager).getTransactionState(ArgumentMatchers.eq(transactionalId))
   }
 
   private def validateRespondsWithConcurrentTransactionsOnInitPidWhenInPrepareState(state: TransactionState): Unit = {
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true).anyTimes()
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
     val metadata = new TransactionMetadata(transactionalId, 0, 0, 0, RecordBatch.NO_PRODUCER_EPOCH, 0, state, mutable.Set[TopicPartition](new TopicPartition("topic", 1)), 0, 0)
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata)))).anyTimes()
-
-    EasyMock.replay(transactionManager)
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata))))
 
     coordinator.handleInitProducerId(transactionalId, 10, None, initProducerIdMockCallback)
 
@@ -1175,32 +1119,29 @@ class TransactionCoordinatorTest {
   }
 
   private def validateIncrementEpochAndUpdateMetadata(state: TransactionState): Unit = {
-    EasyMock.expect(pidGenerator.generateProducerId())
-      .andReturn(producerId)
-      .anyTimes()
+    when(pidGenerator.generateProducerId())
+      .thenReturn(producerId)
 
-    EasyMock.expect(transactionManager.validateTransactionTimeoutMs(EasyMock.anyInt()))
-      .andReturn(true)
+    when(transactionManager.validateTransactionTimeoutMs(anyInt()))
+      .thenReturn(true)
 
     val metadata = new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs, state, mutable.Set.empty[TopicPartition], time.milliseconds(), time.milliseconds())
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata))))
-
-    val capturedNewMetadata: Capture[TxnTransitMetadata] = EasyMock.newCapture()
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.capture(capturedNewMetadata),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()
-    )).andAnswer(() => {
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, metadata))))
+
+    val capturedNewMetadata: ArgumentCaptor[TxnTransitMetadata] = ArgumentCaptor.forClass(classOf[TxnTransitMetadata])
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      capturedNewMetadata.capture(),
+      capturedErrorsCallback.capture(),
+      any(),
+      any()
+    )).thenAnswer(_ => {
       metadata.completeTransitionTo(capturedNewMetadata.getValue)
       capturedErrorsCallback.getValue.apply(Errors.NONE)
     })
 
-    EasyMock.replay(pidGenerator, transactionManager)
-
     val newTxnTimeoutMs = 10
     coordinator.handleInitProducerId(transactionalId, newTxnTimeoutMs, None, initProducerIdMockCallback)
 
@@ -1219,20 +1160,19 @@ class TransactionCoordinatorTest {
     val transition = TxnTransitMetadata(producerId, producerId, producerEpoch, RecordBatch.NO_PRODUCER_EPOCH, txnTimeoutMs,
       transactionState, partitions.toSet, now, now)
 
-    EasyMock.expect(transactionManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, originalMetadata))))
-      .once()
-    EasyMock.expect(transactionManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(transition),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => {
+    when(transactionManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, originalMetadata))))
+    when(transactionManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(transition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
+    ).thenAnswer(_ => {
       if (runCallback)
         capturedErrorsCallback.getValue.apply(Errors.NONE)
-    }).once()
+    })
 
     new TransactionMetadata(transactionalId, producerId, producerId, producerEpoch, RecordBatch.NO_PRODUCER_EPOCH,
       txnTimeoutMs, transactionState, partitions, time.milliseconds(), time.milliseconds())
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerChannelManagerTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerChannelManagerTest.scala
index 0a0ec51..20dbddc 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerChannelManagerTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerChannelManagerTest.scala
@@ -31,18 +31,20 @@ import org.apache.kafka.common.record.RecordBatch
 import org.apache.kafka.common.requests.{RequestHeader, TransactionResult, WriteTxnMarkersRequest, WriteTxnMarkersResponse}
 import org.apache.kafka.common.utils.MockTime
 import org.apache.kafka.common.{Node, TopicPartition}
-import org.easymock.{Capture, EasyMock}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.Mockito.{mock, times, verify, when}
 
 import scala.jdk.CollectionConverters._
 import scala.collection.mutable
 import scala.util.Try
 
 class TransactionMarkerChannelManagerTest {
-  private val metadataCache: MetadataCache = EasyMock.createNiceMock(classOf[MetadataCache])
-  private val networkClient: NetworkClient = EasyMock.createNiceMock(classOf[NetworkClient])
-  private val txnStateManager: TransactionStateManager = EasyMock.mock(classOf[TransactionStateManager])
+  private val metadataCache: MetadataCache = mock(classOf[MetadataCache])
+  private val networkClient: NetworkClient = mock(classOf[NetworkClient])
+  private val txnStateManager: TransactionStateManager = mock(classOf[TransactionStateManager])
 
   private val partition1 = new TopicPartition("topic1", 0)
   private val partition2 = new TopicPartition("topic1", 1)
@@ -65,7 +67,7 @@ class TransactionMarkerChannelManagerTest {
   private val txnMetadata2 = new TransactionMetadata(transactionalId2, producerId2, producerId2, producerEpoch, lastProducerEpoch,
     txnTimeoutMs, PrepareCommit, mutable.Set[TopicPartition](partition1), 0L, 0L)
 
-  private val capturedErrorsCallback: Capture[Errors => Unit] = EasyMock.newCapture()
+  private val capturedErrorsCallback: ArgumentCaptor[Errors => Unit] = ArgumentCaptor.forClass(classOf[Errors => Unit])
   private val time = new MockTime
 
   private val channelManager = new TransactionMarkerChannelManager(
@@ -76,18 +78,14 @@ class TransactionMarkerChannelManagerTest {
     time)
 
   private def mockCache(): Unit = {
-    EasyMock.expect(txnStateManager.partitionFor(transactionalId1))
-      .andReturn(txnTopicPartition1)
-      .anyTimes()
-    EasyMock.expect(txnStateManager.partitionFor(transactionalId2))
-      .andReturn(txnTopicPartition2)
-      .anyTimes()
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId1)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata1))))
-      .anyTimes()
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId2)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata2))))
-      .anyTimes()
+    when(txnStateManager.partitionFor(transactionalId1))
+      .thenReturn(txnTopicPartition1)
+    when(txnStateManager.partitionFor(transactionalId2))
+      .thenReturn(txnTopicPartition2)
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId1)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata1))))
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId2)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata2))))
   }
 
   @Test
@@ -96,25 +94,23 @@ class TransactionMarkerChannelManagerTest {
 
     val expectedTransition = txnMetadata2.prepareComplete(time.milliseconds())
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-
-    EasyMock.expect(txnStateManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId2),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(expectedTransition),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()))
-      .andAnswer(() => {
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+
+    when(txnStateManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any()))
+      .thenAnswer(_ => {
         txnMetadata2.completeTransitionTo(expectedTransition)
         capturedErrorsCallback.getValue.apply(Errors.NONE)
-      }).once()
-
-    EasyMock.replay(txnStateManager, metadataCache)
+      })
 
     var addMarkerFuture: Future[Try[Unit]] = null
     val executor = Executors.newFixedThreadPool(1)
@@ -152,7 +148,13 @@ class TransactionMarkerChannelManagerTest {
     assertTrue(addMarkerFuture.get().isSuccess,
       "Add marker task failed with exception " + addMarkerFuture.get().get)
 
-    EasyMock.verify(txnStateManager)
+    verify(txnStateManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(expectedTransition),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
   }
 
   @Test
@@ -163,20 +165,17 @@ class TransactionMarkerChannelManagerTest {
   @Test
   def shouldGenerateRequestPerPartitionPerBroker(): Unit = {
     mockCache()
-    EasyMock.replay(txnStateManager)
-
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
 
-    EasyMock.replay(metadataCache)
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata1, txnMetadata1.prepareComplete(time.milliseconds()))
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnMetadata2.prepareComplete(time.milliseconds()))
@@ -206,20 +205,17 @@ class TransactionMarkerChannelManagerTest {
   @Test
   def shouldSkipSendMarkersWhenLeaderNotFound(): Unit = {
     mockCache()
-    EasyMock.replay(txnStateManager)
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(None).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
-
-    EasyMock.replay(metadataCache)
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(None)
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata1, txnMetadata1.prepareComplete(time.milliseconds()))
 
@@ -233,25 +229,22 @@ class TransactionMarkerChannelManagerTest {
   @Test
   def shouldSaveForLaterWhenLeaderUnknownButNotAvailable(): Unit = {
     mockCache()
-    EasyMock.replay(txnStateManager)
-
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(Node.noNode))
-      .andReturn(Some(Node.noNode))
-      .andReturn(Some(Node.noNode))
-      .andReturn(Some(Node.noNode))
-      .andReturn(Some(broker1))
-      .andReturn(Some(broker1))
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
-
-    EasyMock.replay(metadataCache)
+
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(Node.noNode))
+      .thenReturn(Some(Node.noNode))
+      .thenReturn(Some(Node.noNode))
+      .thenReturn(Some(Node.noNode))
+      .thenReturn(Some(broker1))
+      .thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata1, txnMetadata1.prepareComplete(time.milliseconds()))
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnMetadata2.prepareComplete(time.milliseconds()))
@@ -287,20 +280,17 @@ class TransactionMarkerChannelManagerTest {
   @Test
   def shouldRemoveMarkersForTxnPartitionWhenPartitionEmigrated(): Unit = {
     mockCache()
-    EasyMock.replay(txnStateManager)
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
-
-    EasyMock.replay(metadataCache)
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata1, txnMetadata1.prepareComplete(time.milliseconds()))
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnMetadata2.prepareComplete(time.milliseconds()))
@@ -328,31 +318,30 @@ class TransactionMarkerChannelManagerTest {
   def shouldCompleteAppendToLogOnEndTxnWhenSendMarkersSucceed(): Unit = {
     mockCache()
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     val txnTransitionMetadata2 = txnMetadata2.prepareComplete(time.milliseconds())
 
-    EasyMock.expect(txnStateManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId2),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(txnTransitionMetadata2),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()))
-      .andAnswer(() => {
+    when(txnStateManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any()))
+      .thenAnswer(_ => {
         txnMetadata2.completeTransitionTo(txnTransitionMetadata2)
         capturedErrorsCallback.getValue.apply(Errors.NONE)
-      }).once()
-    EasyMock.replay(txnStateManager, metadataCache)
+      })
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnTransitionMetadata2)
 
@@ -364,7 +353,13 @@ class TransactionMarkerChannelManagerTest {
         null, null, 0, 0, false, null, null, response))
     }
 
-    EasyMock.verify(txnStateManager)
+    verify(txnStateManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
 
     assertEquals(0, channelManager.numTxnsWithPendingMarkers)
     assertEquals(0, channelManager.queueForBroker(broker1.id).get.totalNumMarkers)
@@ -376,31 +371,30 @@ class TransactionMarkerChannelManagerTest {
   def shouldAbortAppendToLogOnEndTxnWhenNotCoordinatorError(): Unit = {
     mockCache()
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     val txnTransitionMetadata2 = txnMetadata2.prepareComplete(time.milliseconds())
 
-    EasyMock.expect(txnStateManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId2),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(txnTransitionMetadata2),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()))
-      .andAnswer(() => {
+    when(txnStateManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any()))
+      .thenAnswer(_ => {
         txnMetadata2.pendingState = None
         capturedErrorsCallback.getValue.apply(Errors.NOT_COORDINATOR)
-      }).once()
-    EasyMock.replay(txnStateManager, metadataCache)
+      })
 
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnTransitionMetadata2)
 
@@ -412,7 +406,13 @@ class TransactionMarkerChannelManagerTest {
         null, null, 0, 0, false, null, null, response))
     }
 
-    EasyMock.verify(txnStateManager)
+    verify(txnStateManager).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
 
     assertEquals(0, channelManager.numTxnsWithPendingMarkers)
     assertEquals(0, channelManager.queueForBroker(broker1.id).get.totalNumMarkers)
@@ -424,34 +424,32 @@ class TransactionMarkerChannelManagerTest {
   def shouldRetryAppendToLogOnEndTxnWhenCoordinatorNotAvailableError(): Unit = {
     mockCache()
 
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition1.topic),
-      EasyMock.eq(partition1.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker1)).anyTimes()
-    EasyMock.expect(metadataCache.getPartitionLeaderEndpoint(
-      EasyMock.eq(partition2.topic),
-      EasyMock.eq(partition2.partition),
-      EasyMock.anyObject())
-    ).andReturn(Some(broker2)).anyTimes()
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition1.topic),
+      ArgumentMatchers.eq(partition1.partition),
+      any())
+    ).thenReturn(Some(broker1))
+    when(metadataCache.getPartitionLeaderEndpoint(
+      ArgumentMatchers.eq(partition2.topic),
+      ArgumentMatchers.eq(partition2.partition),
+      any())
+    ).thenReturn(Some(broker2))
 
     val txnTransitionMetadata2 = txnMetadata2.prepareComplete(time.milliseconds())
 
-    EasyMock.expect(txnStateManager.appendTransactionToLog(
-      EasyMock.eq(transactionalId2),
-      EasyMock.eq(coordinatorEpoch),
-      EasyMock.eq(txnTransitionMetadata2),
-      EasyMock.capture(capturedErrorsCallback),
-      EasyMock.anyObject(),
-      EasyMock.anyObject()))
-      .andAnswer(() => capturedErrorsCallback.getValue.apply(Errors.COORDINATOR_NOT_AVAILABLE))
-      .andAnswer(() => {
+    when(txnStateManager.appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any()))
+      .thenAnswer(_ => capturedErrorsCallback.getValue.apply(Errors.COORDINATOR_NOT_AVAILABLE))
+      .thenAnswer(_ => {
         txnMetadata2.completeTransitionTo(txnTransitionMetadata2)
         capturedErrorsCallback.getValue.apply(Errors.NONE)
       })
 
-    EasyMock.replay(txnStateManager, metadataCache)
-
     channelManager.addTxnMarkersToSend(coordinatorEpoch, txnResult, txnMetadata2, txnTransitionMetadata2)
 
     val requestAndHandlers: Iterable[RequestAndCompletionHandler] = channelManager.generateRequests()
@@ -465,7 +463,13 @@ class TransactionMarkerChannelManagerTest {
     // call this again so that append log will be retried
     channelManager.generateRequests()
 
-    EasyMock.verify(txnStateManager)
+    verify(txnStateManager, times(2)).appendTransactionToLog(
+      ArgumentMatchers.eq(transactionalId2),
+      ArgumentMatchers.eq(coordinatorEpoch),
+      ArgumentMatchers.eq(txnTransitionMetadata2),
+      capturedErrorsCallback.capture(),
+      any(),
+      any())
 
     assertEquals(0, channelManager.numTxnsWithPendingMarkers)
     assertEquals(0, channelManager.queueForBroker(broker1.id).get.totalNumMarkers)
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerRequestCompletionHandlerTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerRequestCompletionHandlerTest.scala
index d15f3b2..2fc2d85 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerRequestCompletionHandlerTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionMarkerRequestCompletionHandlerTest.scala
@@ -24,9 +24,10 @@ import org.apache.kafka.common.TopicPartition
 import org.apache.kafka.common.protocol.{ApiKeys, Errors}
 import org.apache.kafka.common.record.RecordBatch
 import org.apache.kafka.common.requests.{RequestHeader, TransactionResult, WriteTxnMarkersRequest, WriteTxnMarkersResponse}
-import org.easymock.EasyMock
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.{mock, verify, when}
 
 import scala.collection.mutable
 
@@ -49,40 +50,33 @@ class TransactionMarkerRequestCompletionHandlerTest {
     txnTimeoutMs, PrepareCommit, mutable.Set[TopicPartition](topicPartition), 0L, 0L)
 
   private val markerChannelManager: TransactionMarkerChannelManager =
-    EasyMock.createNiceMock(classOf[TransactionMarkerChannelManager])
+    mock(classOf[TransactionMarkerChannelManager])
 
-  private val txnStateManager: TransactionStateManager = EasyMock.createNiceMock(classOf[TransactionStateManager])
+  private val txnStateManager: TransactionStateManager = mock(classOf[TransactionStateManager])
 
   private val handler = new TransactionMarkerRequestCompletionHandler(brokerId, txnStateManager, markerChannelManager, txnIdAndMarkers)
 
   private def mockCache(): Unit = {
-    EasyMock.expect(txnStateManager.partitionFor(transactionalId))
-      .andReturn(txnTopicPartition)
-      .anyTimes()
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
-      .anyTimes()
-    EasyMock.replay(txnStateManager)
+    when(txnStateManager.partitionFor(transactionalId))
+      .thenReturn(txnTopicPartition)
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch, txnMetadata))))
   }
 
   @Test
   def shouldReEnqueuePartitionsWhenBrokerDisconnected(): Unit = {
     mockCache()
 
-    EasyMock.expect(markerChannelManager.addTxnMarkersToBrokerQueue(transactionalId,
-      producerId, producerEpoch, txnResult, coordinatorEpoch, Set[TopicPartition](topicPartition)))
-    EasyMock.replay(markerChannelManager)
-
     handler.onComplete(new ClientResponse(new RequestHeader(ApiKeys.PRODUCE, 0, "client", 1),
       null, null, 0, 0, true, null, null, null))
 
-    EasyMock.verify(markerChannelManager)
+    verify(markerChannelManager).addTxnMarkersToBrokerQueue(transactionalId,
+      producerId, producerEpoch, txnResult, coordinatorEpoch, Set[TopicPartition](topicPartition))
   }
 
   @Test
   def shouldThrowIllegalStateExceptionIfErrorCodeNotAvailableForPid(): Unit = {
     mockCache()
-    EasyMock.replay(markerChannelManager)
 
     val response = new WriteTxnMarkersResponse(new java.util.HashMap[java.lang.Long, java.util.Map[TopicPartition, Errors]]())
 
@@ -99,30 +93,24 @@ class TransactionMarkerRequestCompletionHandlerTest {
 
   @Test
   def shouldCompleteDelayedOperationWhenNotCoordinator(): Unit = {
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.NOT_COORDINATOR))
-      .anyTimes()
-    EasyMock.replay(txnStateManager)
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.NOT_COORDINATOR))
 
     verifyRemoveDelayedOperationOnError(Errors.NONE)
   }
 
   @Test
   def shouldCompleteDelayedOperationWhenCoordinatorLoading(): Unit = {
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
-      .anyTimes()
-    EasyMock.replay(txnStateManager)
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Left(Errors.COORDINATOR_LOAD_IN_PROGRESS))
 
     verifyRemoveDelayedOperationOnError(Errors.NONE)
   }
 
   @Test
   def shouldCompleteDelayedOperationWhenCoordinatorEpochChanged(): Unit = {
-    EasyMock.expect(txnStateManager.getTransactionState(EasyMock.eq(transactionalId)))
-      .andReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch+1, txnMetadata))))
-      .anyTimes()
-    EasyMock.replay(txnStateManager)
+    when(txnStateManager.getTransactionState(ArgumentMatchers.eq(transactionalId)))
+      .thenReturn(Right(Some(CoordinatorEpochAndTxnMetadata(coordinatorEpoch+1, txnMetadata))))
 
     verifyRemoveDelayedOperationOnError(Errors.NONE)
   }
@@ -197,19 +185,16 @@ class TransactionMarkerRequestCompletionHandlerTest {
     verifyCompleteDelayedOperationOnError(Errors.UNSUPPORTED_FOR_MESSAGE_FORMAT)
   }
 
-  private def verifyRetriesPartitionOnError(error: Errors) = {
+  private def verifyRetriesPartitionOnError(error: Errors): Unit = {
     mockCache()
 
-    EasyMock.expect(markerChannelManager.addTxnMarkersToBrokerQueue(transactionalId,
-      producerId, producerEpoch, txnResult, coordinatorEpoch, Set[TopicPartition](topicPartition)))
-    EasyMock.replay(markerChannelManager)
-
     val response = new WriteTxnMarkersResponse(createProducerIdErrorMap(error))
     handler.onComplete(new ClientResponse(new RequestHeader(ApiKeys.PRODUCE, 0, "client", 1),
       null, null, 0, 0, false, null, null, response))
 
     assertEquals(txnMetadata.topicPartitions, mutable.Set[TopicPartition](topicPartition))
-    EasyMock.verify(markerChannelManager)
+    verify(markerChannelManager).addTxnMarkersToBrokerQueue(transactionalId,
+      producerId, producerEpoch, txnResult, coordinatorEpoch, Set[TopicPartition](topicPartition))
   }
 
   private def verifyThrowIllegalStateExceptionOnError(error: Errors) = {
@@ -223,10 +208,8 @@ class TransactionMarkerRequestCompletionHandlerTest {
   private def verifyCompleteDelayedOperationOnError(error: Errors): Unit = {
 
     var completed = false
-    EasyMock.expect(markerChannelManager.maybeWriteTxnCompletion(transactionalId))
-      .andAnswer(() => completed = true)
-      .once()
-    EasyMock.replay(markerChannelManager)
+    when(markerChannelManager.maybeWriteTxnCompletion(transactionalId))
+      .thenAnswer(_ => completed = true)
 
     val response = new WriteTxnMarkersResponse(createProducerIdErrorMap(error))
     handler.onComplete(new ClientResponse(new RequestHeader(ApiKeys.PRODUCE, 0, "client", 1),
@@ -239,10 +222,8 @@ class TransactionMarkerRequestCompletionHandlerTest {
   private def verifyRemoveDelayedOperationOnError(error: Errors): Unit = {
 
     var removed = false
-    EasyMock.expect(markerChannelManager.removeMarkersForTxnId(transactionalId))
-      .andAnswer(() => removed = true)
-      .once()
-    EasyMock.replay(markerChannelManager)
+    when(markerChannelManager.removeMarkersForTxnId(transactionalId))
+      .thenAnswer(_ => removed = true)
 
     val response = new WriteTxnMarkersResponse(createProducerIdErrorMap(error))
     handler.onComplete(new ClientResponse(new RequestHeader(ApiKeys.PRODUCE, 0, "client", 1),
diff --git a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionStateManagerTest.scala b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionStateManagerTest.scala
index 32e41cd..6a56b76 100644
--- a/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionStateManagerTest.scala
+++ b/core/src/test/scala/unit/kafka/coordinator/transaction/TransactionStateManagerTest.scala
@@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch
 import java.util.concurrent.locks.ReentrantLock
 
 import javax.management.ObjectName
-import kafka.log.{AppendOrigin, Defaults, UnifiedLog, LogConfig}
+import kafka.log.{AppendOrigin, Defaults, LogConfig, UnifiedLog}
 import kafka.server.{FetchDataInfo, FetchLogEnd, LogOffsetMetadata, ReplicaManager, RequestLocal}
 import kafka.utils.{MockScheduler, Pool, TestUtils}
 import kafka.zk.KafkaZkClient
@@ -34,9 +34,11 @@ import org.apache.kafka.common.record._
 import org.apache.kafka.common.requests.ProduceResponse.PartitionResponse
 import org.apache.kafka.common.requests.TransactionResult
 import org.apache.kafka.common.utils.MockTime
-import org.easymock.{Capture, EasyMock, IAnswer}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
+import org.mockito.{ArgumentCaptor, ArgumentMatchers}
+import org.mockito.ArgumentMatchers.{any, anyInt, anyLong, anyShort}
+import org.mockito.Mockito.{atLeastOnce, mock, reset, times, verify, when}
 
 import scala.collection.{Map, mutable}
 import scala.jdk.CollectionConverters._
@@ -53,14 +55,12 @@ class TransactionStateManagerTest {
 
   val time = new MockTime()
   val scheduler = new MockScheduler(time)
-  val zkClient: KafkaZkClient = EasyMock.createNiceMock(classOf[KafkaZkClient])
-  val replicaManager: ReplicaManager = EasyMock.createNiceMock(classOf[ReplicaManager])
+  val zkClient: KafkaZkClient = mock(classOf[KafkaZkClient])
+  val replicaManager: ReplicaManager = mock(classOf[ReplicaManager])
 
-  EasyMock.expect(zkClient.getTopicPartitionCount(TRANSACTION_STATE_TOPIC_NAME))
-    .andReturn(Some(numPartitions))
-    .anyTimes()
-
-  EasyMock.replay(zkClient)
+  when(zkClient.getTopicPartitionCount(TRANSACTION_STATE_TOPIC_NAME))
+    .thenReturn(Some(numPartitions))
+  
   val metrics = new Metrics()
 
   val txnConfig = TransactionConfig()
@@ -87,7 +87,6 @@ class TransactionStateManagerTest {
 
   @AfterEach
   def tearDown(): Unit = {
-    EasyMock.reset(zkClient, replicaManager)
     transactionManager.shutdown()
   }
 
@@ -149,16 +148,16 @@ class TransactionStateManagerTest {
     val startOffset = 0L
     val endOffset = 1L
 
-    val fileRecordsMock = EasyMock.mock[FileRecords](classOf[FileRecords])
-    val logMock = EasyMock.mock[UnifiedLog](classOf[UnifiedLog])
-    EasyMock.expect(replicaManager.getLog(topicPartition)).andStubReturn(Some(logMock))
-    EasyMock.expect(logMock.logStartOffset).andStubReturn(startOffset)
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true))
-    ).andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
-    EasyMock.expect(replicaManager.getLogEndOffset(topicPartition)).andStubReturn(Some(endOffset))
+    val fileRecordsMock = mock[FileRecords](classOf[FileRecords])
+    val logMock = mock[UnifiedLog](classOf[UnifiedLog])
+    when(replicaManager.getLog(topicPartition)).thenReturn(Some(logMock))
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true))
+    ).thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
+    when(replicaManager.getLogEndOffset(topicPartition)).thenReturn(Some(endOffset))
 
     txnMetadata1.state = PrepareCommit
     txnMetadata1.addPartitions(Set[TopicPartition](
@@ -171,20 +170,15 @@ class TransactionStateManagerTest {
     // is triggered before the loading returns
     val latch = new CountDownLatch(1)
 
-    EasyMock.expect(fileRecordsMock.sizeInBytes()).andStubReturn(records.sizeInBytes)
-    val bufferCapture = EasyMock.newCapture[ByteBuffer]
-    fileRecordsMock.readInto(EasyMock.capture(bufferCapture), EasyMock.anyInt())
-    EasyMock.expectLastCall().andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = {
-        latch.await()
-        val buffer = bufferCapture.getValue
-        buffer.put(records.buffer.duplicate)
-        buffer.flip()
-      }
+    when(fileRecordsMock.sizeInBytes()).thenReturn(records.sizeInBytes)
+    val bufferCapture: ArgumentCaptor[ByteBuffer] = ArgumentCaptor.forClass(classOf[ByteBuffer])
+    when(fileRecordsMock.readInto(bufferCapture.capture(), anyInt())).thenAnswer(_ => {
+      latch.await()
+      val buffer = bufferCapture.getValue
+      buffer.put(records.buffer.duplicate)
+      buffer.flip()
     })
 
-    EasyMock.replay(logMock, fileRecordsMock, replicaManager)
-
     val coordinatorEpoch = 0
     val partitionAndLeaderEpoch = TransactionPartitionAndLeaderEpoch(partitionId, coordinatorEpoch)
 
@@ -642,16 +636,25 @@ class TransactionStateManagerTest {
     loadTransactionsForPartitions(partitionIds)
     val allTransactionalIds = loadExpiredTransactionalIds(numTransactionalIds = 20)
 
-    EasyMock.reset(replicaManager)
+    reset(replicaManager)
     expectLogConfig(partitionIds, maxBatchSize)
 
     val attemptedAppends = mutable.Map.empty[TopicPartition, mutable.Buffer[MemoryRecords]]
     expectTransactionalIdExpiration(Errors.MESSAGE_TOO_LARGE, attemptedAppends)
-    EasyMock.replay(replicaManager)
 
     assertEquals(allTransactionalIds, listExpirableTransactionalIds())
     transactionManager.removeExpiredTransactionalIds()
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, atLeastOnce()).appendRecords(
+      anyLong(),
+      ArgumentMatchers.eq((-1).toShort),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any()
+    )
 
     for (batches <- attemptedAppends.values; batch <- batches) {
       assertTrue(batch.sizeInBytes() > maxBatchSize)
@@ -671,21 +674,30 @@ class TransactionStateManagerTest {
     loadTransactionsForPartitions(partitionIds)
     val allTransactionalIds = loadExpiredTransactionalIds(numTransactionalIds = 20)
 
-    EasyMock.reset(replicaManager)
+    reset(replicaManager)
 
     // Partition 0 returns log config as normal
     expectLogConfig(Seq(onlinePartitionId), maxBatchSize)
     // No log config returned for partition 0 since it is offline
-    EasyMock.expect(replicaManager.getLogConfig(new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, offlinePartitionId)))
-      .andStubReturn(None)
+    when(replicaManager.getLogConfig(new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, offlinePartitionId)))
+      .thenReturn(None)
 
     val appendedRecords = mutable.Map.empty[TopicPartition, mutable.Buffer[MemoryRecords]]
     expectTransactionalIdExpiration(Errors.NONE, appendedRecords)
-    EasyMock.replay(replicaManager)
 
     assertEquals(allTransactionalIds, listExpirableTransactionalIds())
     transactionManager.removeExpiredTransactionalIds()
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).appendRecords(
+      anyLong(),
+      ArgumentMatchers.eq((-1).toShort),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any()
+    )
 
     assertEquals(Set(onlinePartitionId), appendedRecords.keySet.map(_.partition))
 
@@ -707,16 +719,24 @@ class TransactionStateManagerTest {
     loadTransactionsForPartitions(partitionIds)
     val allTransactionalIds = loadExpiredTransactionalIds(numTransactionalIds = 1000)
 
-    EasyMock.reset(replicaManager)
+    reset(replicaManager)
     expectLogConfig(partitionIds, maxBatchSize)
 
     val appendedRecords = mutable.Map.empty[TopicPartition, mutable.Buffer[MemoryRecords]]
     expectTransactionalIdExpiration(Errors.NONE, appendedRecords)
-    EasyMock.replay(replicaManager)
 
     assertEquals(allTransactionalIds, listExpirableTransactionalIds())
     transactionManager.removeExpiredTransactionalIds()
-    EasyMock.verify(replicaManager)
+    verify(replicaManager, atLeastOnce()).appendRecords(
+      anyLong(),
+      ArgumentMatchers.eq((-1).toShort),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any(),
+      any(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
 
     assertEquals(Set.empty, listExpirableTransactionalIds())
     assertEquals(partitionIds.toSet, appendedRecords.keys.map(_.partition))
@@ -801,7 +821,7 @@ class TransactionStateManagerTest {
     prepareTxnLog(topicPartition, 0, records)
     transactionManager.loadTransactionsForTxnTopicPartition(partitionId, coordinatorEpoch = 1, (_, _, _, _) => ())
     assertEquals(0, transactionManager.loadingPartitions.size)
-    assertTrue(transactionManager.transactionMetadataCache.get(partitionId).isDefined)
+    assertTrue(transactionManager.transactionMetadataCache.contains(partitionId))
     assertEquals(1, transactionManager.transactionMetadataCache.get(partitionId).get.coordinatorEpoch)
   }
 
@@ -813,26 +833,28 @@ class TransactionStateManagerTest {
     val startOffset = 0L
     val endOffset = 10L
 
-    val logMock: UnifiedLog = EasyMock.mock(classOf[UnifiedLog])
-    EasyMock.expect(replicaManager.getLog(topicPartition)).andStubReturn(Some(logMock))
-    EasyMock.expect(logMock.logStartOffset).andStubReturn(startOffset)
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true))
-    ).andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), MemoryRecords.EMPTY))
-    EasyMock.expect(replicaManager.getLogEndOffset(topicPartition)).andStubReturn(Some(endOffset))
-
-    EasyMock.replay(logMock)
-    EasyMock.replay(replicaManager)
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    when(replicaManager.getLog(topicPartition)).thenReturn(Some(logMock))
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true))
+    ).thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), MemoryRecords.EMPTY))
+    when(replicaManager.getLogEndOffset(topicPartition)).thenReturn(Some(endOffset))
 
     transactionManager.loadTransactionsForTxnTopicPartition(partitionId, coordinatorEpoch = 0, (_, _, _, _) => ())
 
     // let the time advance to trigger the background thread loading
     scheduler.tick()
 
-    EasyMock.verify(logMock)
-    EasyMock.verify(replicaManager)
+    verify(replicaManager).getLog(topicPartition)
+    verify(logMock).logStartOffset
+    verify(logMock).read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true))
+    verify(replicaManager, times(2)).getLogEndOffset(topicPartition)
     assertEquals(0, transactionManager.loadingPartitions.size)
   }
 
@@ -857,20 +879,20 @@ class TransactionStateManagerTest {
     appendError: Errors,
     capturedAppends: mutable.Map[TopicPartition, mutable.Buffer[MemoryRecords]]
   ): Unit = {
-    val recordsCapture: Capture[Map[TopicPartition, MemoryRecords]] = EasyMock.newCapture()
-    val callbackCapture: Capture[Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-
-    EasyMock.expect(replicaManager.appendRecords(
-      EasyMock.anyLong(),
-      EasyMock.eq((-1).toShort),
-      EasyMock.eq(true),
-      EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.capture(recordsCapture),
-      EasyMock.capture(callbackCapture),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject()
-    )).andAnswer(() => callbackCapture.getValue.apply(
+    val recordsCapture: ArgumentCaptor[Map[TopicPartition, MemoryRecords]] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, MemoryRecords]])
+    val callbackCapture: ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, PartitionResponse] => Unit])
+
+    when(replicaManager.appendRecords(
+      anyLong(),
+      ArgumentMatchers.eq((-1).toShort),
+      ArgumentMatchers.eq(true),
+      ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      recordsCapture.capture(),
+      callbackCapture.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any()
+    )).thenAnswer(_ => callbackCapture.getValue.apply(
       recordsCapture.getValue.map { case (topicPartition, records) =>
         val batches = capturedAppends.getOrElse(topicPartition, {
           val batches = mutable.Buffer.empty[MemoryRecords]
@@ -882,7 +904,7 @@ class TransactionStateManagerTest {
 
         topicPartition -> new PartitionResponse(appendError, 0L, RecordBatch.NO_TIMESTAMP, 0L)
       }.toMap
-    )).anyTimes()
+    ))
   }
 
   private def loadTransactionsForPartitions(
@@ -897,15 +919,13 @@ class TransactionStateManagerTest {
     partitionIds: Seq[Int],
     maxBatchSize: Int
   ): Unit = {
-    val logConfig: LogConfig = EasyMock.mock(classOf[LogConfig])
-    EasyMock.expect(logConfig.maxMessageSize).andStubReturn(maxBatchSize)
+    val logConfig: LogConfig = mock(classOf[LogConfig])
+    when(logConfig.maxMessageSize).thenReturn(maxBatchSize)
 
     for (partitionId <- partitionIds) {
-      EasyMock.expect(replicaManager.getLogConfig(new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, partitionId)))
-        .andStubReturn(Some(logConfig))
+      when(replicaManager.getLogConfig(new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, partitionId)))
+        .thenReturn(Some(logConfig))
     }
-
-    EasyMock.replay(logConfig)
   }
 
   private def setupAndRunTransactionalIdExpiration(error: Errors, txnState: TransactionState): Unit = {
@@ -924,9 +944,7 @@ class TransactionStateManagerTest {
     val appendedRecords = mutable.Map.empty[TopicPartition, mutable.Buffer[MemoryRecords]]
     expectTransactionalIdExpiration(error, appendedRecords)
 
-    EasyMock.replay(replicaManager)
     transactionManager.removeExpiredTransactionalIds()
-    EasyMock.verify(replicaManager)
 
     val stateAllowsExpiration = txnState match {
       case Empty | CompleteCommit | CompleteAbort => true
@@ -984,58 +1002,52 @@ class TransactionStateManagerTest {
   private def prepareTxnLog(topicPartition: TopicPartition,
                             startOffset: Long,
                             records: MemoryRecords): Unit = {
-    EasyMock.reset(replicaManager)
+    reset(replicaManager)
 
-    val logMock: UnifiedLog = EasyMock.mock(classOf[UnifiedLog])
-    val fileRecordsMock: FileRecords = EasyMock.mock(classOf[FileRecords])
+    val logMock: UnifiedLog = mock(classOf[UnifiedLog])
+    val fileRecordsMock: FileRecords = mock(classOf[FileRecords])
 
     val endOffset = startOffset + records.records.asScala.size
 
-    EasyMock.expect(replicaManager.getLog(topicPartition)).andStubReturn(Some(logMock))
-    EasyMock.expect(replicaManager.getLogEndOffset(topicPartition)).andStubReturn(Some(endOffset))
-
-    EasyMock.expect(logMock.logStartOffset).andStubReturn(startOffset)
-    EasyMock.expect(logMock.read(EasyMock.eq(startOffset),
-      maxLength = EasyMock.anyInt(),
-      isolation = EasyMock.eq(FetchLogEnd),
-      minOneMessage = EasyMock.eq(true)))
-      .andReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
-
-    EasyMock.expect(fileRecordsMock.sizeInBytes()).andStubReturn(records.sizeInBytes)
-
-    val bufferCapture = EasyMock.newCapture[ByteBuffer]
-    fileRecordsMock.readInto(EasyMock.capture(bufferCapture), EasyMock.anyInt())
-    EasyMock.expectLastCall().andAnswer(new IAnswer[Unit] {
-      override def answer: Unit = {
-        val buffer = bufferCapture.getValue
-        buffer.put(records.buffer.duplicate)
-        buffer.flip()
-      }
+    when(replicaManager.getLog(topicPartition)).thenReturn(Some(logMock))
+    when(replicaManager.getLogEndOffset(topicPartition)).thenReturn(Some(endOffset))
+
+    when(logMock.logStartOffset).thenReturn(startOffset)
+    when(logMock.read(ArgumentMatchers.eq(startOffset),
+      maxLength = anyInt(),
+      isolation = ArgumentMatchers.eq(FetchLogEnd),
+      minOneMessage = ArgumentMatchers.eq(true)))
+      .thenReturn(FetchDataInfo(LogOffsetMetadata(startOffset), fileRecordsMock))
+
+    when(fileRecordsMock.sizeInBytes()).thenReturn(records.sizeInBytes)
+
+    val bufferCapture: ArgumentCaptor[ByteBuffer] = ArgumentCaptor.forClass(classOf[ByteBuffer])
+    when(fileRecordsMock.readInto(bufferCapture.capture(), anyInt())).thenAnswer(_ => {
+      val buffer = bufferCapture.getValue
+      buffer.put(records.buffer.duplicate)
+      buffer.flip()
     })
-    EasyMock.replay(logMock, fileRecordsMock, replicaManager)
   }
 
   private def prepareForTxnMessageAppend(error: Errors): Unit = {
-    EasyMock.reset(replicaManager)
-
-    val capturedArgument: Capture[Map[TopicPartition, PartitionResponse] => Unit] = EasyMock.newCapture()
-    EasyMock.expect(replicaManager.appendRecords(EasyMock.anyLong(),
-      EasyMock.anyShort(),
-      internalTopicsAllowed = EasyMock.eq(true),
-      origin = EasyMock.eq(AppendOrigin.Coordinator),
-      EasyMock.anyObject().asInstanceOf[Map[TopicPartition, MemoryRecords]],
-      EasyMock.capture(capturedArgument),
-      EasyMock.anyObject().asInstanceOf[Option[ReentrantLock]],
-      EasyMock.anyObject(),
-      EasyMock.anyObject())
-    ).andAnswer(() => capturedArgument.getValue.apply(
+    reset(replicaManager)
+
+    val capturedArgument: ArgumentCaptor[Map[TopicPartition, PartitionResponse] => Unit] = ArgumentCaptor.forClass(classOf[Map[TopicPartition, PartitionResponse] => Unit])
+    when(replicaManager.appendRecords(anyLong(),
+      anyShort(),
+      internalTopicsAllowed = ArgumentMatchers.eq(true),
+      origin = ArgumentMatchers.eq(AppendOrigin.Coordinator),
+      any[Map[TopicPartition, MemoryRecords]],
+      capturedArgument.capture(),
+      any[Option[ReentrantLock]],
+      any(),
+      any())
+    ).thenAnswer(_ => capturedArgument.getValue.apply(
       Map(new TopicPartition(TRANSACTION_STATE_TOPIC_NAME, partitionId) ->
         new PartitionResponse(error, 0L, RecordBatch.NO_TIMESTAMP, 0L)))
     )
-    EasyMock.expect(replicaManager.getMagic(EasyMock.anyObject()))
-      .andStubReturn(Some(RecordBatch.MAGIC_VALUE_V1))
-
-    EasyMock.replay(replicaManager)
+    when(replicaManager.getMagic(any()))
+      .thenReturn(Some(RecordBatch.MAGIC_VALUE_V1))
   }
 
   @Test