java - 在调用 void ServiceImplementationLayer 方法的 JUnit 测试用例上抛出 Mockito UnfinishedStubbingException
问题描述
我在一个 Maven 项目中使用 Mockito/JUnit 来实现一个基于控制台的应用程序,它具有 DAO 设计模式。我的 IDE 是 Spring Tools Suite 3。
问题是我UnfinishedStubbingException
每次在我的特定 JUnit 测试上运行这个特定测试时都会得到一个,我不知道为什么会这样,因为语法看起来是正确的。我对单元测试和 Mockito非常陌生,但我认为这是因为从一层到下一层的抽象级别由于某种原因使 Mockito 感到困惑。因此,我最初尝试在测试用例中的服务对象上使用Spy与MockNotAMockException
(但随后被抛出)。
任何如何解决此问题的建议和/或建议将不胜感激。
这是堆栈跟踪:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.revature.testing.BankAccountEvaluationService.testMakeDeposit_ValidUserId(BankAccountEvaluationService.java:91)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
at com.revature.testing.BankAccountEvaluationService.testMakeDeposit_ValidUserId(BankAccountEvaluationService.java:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
这是示例代码:
BankAccountEvaluationTest
班级:
@InjectMocks
private AccountServiceImpl service;
@Mock
private AccountDaoImpl daoMock;
@Before
public void setUp() {
service = Mockito.spy(new AccountServiceImpl());
MockitoAnnotations.initMocks(this);
}
@Test
public void testMakeDeposit_ValidUserId() {
//setup
Account account = Mockito.mock(Account.class);
account.setAccountId(1);
double amount = 20.50;
//gives UnfinishedStubbingException -> Mockito doesn't like this because it's mocking within a mocking object
//doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber); //Solution?
//run
service.makeDeposit(amount, account.getAccountId());
//verify
verify(service, times(1)).makeDeposit(amount, account.getAccountId());
}
AccountServiceImpl
班级:
package com.revature.serviceimpl;
import java.util.List;
import org.apache.log4j.Logger;
import com.revature.dao.AccountDao;
import com.revature.daoimpl.AccountDaoImpl;
import com.revature.model.Account;
import com.revature.service.AccountService;
public class AccountServiceImpl implements AccountService {
private static Logger logger = Logger.getLogger(AccountServiceImpl.class);
private AccountDao accountDao = new AccountDaoImpl();
//other overridden methods from AccountService interface
@Override
public void makeDeposit(double addedCash, int id) {
logger.info("Sending deposit request to the database.");
// find the account
Account account = accountDao.selectAccountByAccountId(id);
System.out.println(account);
// set new balance
account.setBalance(account.getBalance() + addedCash);
double myNewBalance = account.getBalance();
logger.info("New balance: " + account.getBalance());
logger.info("Updating account balance to account number " + id);
// update the database
accountDao.updateAccountBalance(myNewBalance, id);
}
}
AccountDaoImpl
班级:
package com.revature.daoimpl;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.revature.dao.AccountDao;
import com.revature.model.Account;
import com.revature.model.AccountStatus;
import com.revature.model.AccountType;
public class AccountDaoImpl implements AccountDao {
private static Logger logger = Logger.getLogger(UserDaoImpl.class);
private static String url = MY_URL;
private static String dbUsername = MY_DATABASE_NAME;
private static String dbPassword = MY_DATABASE_PASSWORD;
//other overridden methods from AccountDao interface
@Override
public void updateAccountBalance(double balance, int id) {
try (Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword)) {
String sql = "UPDATE accounts SET account_balance = ? WHERE account_id = ?;";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDouble(1, balance);
ps.setInt(2, id);
ps.executeUpdate();
logger.info("new balance is now set");
} catch (SQLException e) {
logger.warn("Error in SQL execution to update balance. Stack Trace: ", e);
}
}
}
解决方案
自从我上次使用 Mockito 以来已经有一段时间了,但可能是这样而不是
doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber);
你应该使用
doNothing().when(daoMock).updateAccountBalance(Mockito.any(), Mockito.any());
...也许你也可以试试
when(daoMock.updateAccountBalance(Mockito.any(), Mockito.any())).doNothing();
我认为(但我不确定)您只是在使用参数 (account.getBalance() + amount, accountNumber) 模拟确切的调用
因此,例如,如果您在设置模拟时的帐号是 5,那么您只是在模拟一个帐号 = 5 的呼叫