重新封装RedisDistributedLock上锁的逻辑

重新封装RedisDistributedLock上锁的逻辑

Content #

Java中有Transaction类定义如下:

public class Transaction {
  // ...fields
  // ...get() methods...
  // ...contructor

  public boolean execute() throws InvalidTransactionException {
    boolean isLocked = false;
    try {
      isLocked = RedisDistributedLock.getSingletonIntance().lockTransction(id);
      if (!isLocked) {
        return false; // 锁定未成功,返回false,job兜底执行
      }
      //...
    } finally {
      if (isLocked) {
       RedisDistributedLock.getSingletonIntance().unlockTransction(id);
      }
    }
  }
}

现在要对execute方法做单元测试,由于RedisDistributedLock 是一个单例类,单例相当于一个全局变量,无法 mock(无法继承和重写方法),也无法通过依赖注入的方式来替换。

可以对 transaction 上锁这部分逻辑重新封装一下。具体代码实现如下所示:

public class TransactionLock {
  public boolean lock(String id) {
    return RedisDistributedLock.getSingletonIntance().lockTransction(id);
  }

  public void unlock() {
    RedisDistributedLock.getSingletonIntance().unlockTransction(id);
  }
}
public class Transaction {
  //...
  private TransactionLock lock;

  public void setTransactionLock(TransactionLock lock) {
    this.lock = lock;
  }

  public boolean execute() {
    //...
    try {
      isLocked = lock.lock();
      //...
    } finally {
      if (isLocked) {
        lock.unlock();
      }
    }
    //...
  }
}

针对重构过的代码,单元测试代码修改为下面这个样子。

public void testExecute() {
  Long buyerId = 123L;
  Long sellerId = 234L;
  Long productId = 345L;
  Long orderId = 456L;

  TransactionLock mockLock = new TransactionLock() {
    public boolean lock(String id) {
      return true;
    }

    public void unlock() {}
  };

  Transction transaction = new Transaction(null, buyerId, sellerId, productId, orderId);
  transaction.setWalletRpcService(new MockWalletRpcServiceOne());
  transaction.setTransactionLock(mockLock);
  boolean executedResult = transaction.execute();
  assertTrue(executedResult);
  assertEquals(STATUS.EXECUTED, transaction.getStatus());
}

From #