如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法
Posted
技术标签:
【中文标题】如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法【英文标题】:How to mock private method in public method in Spring Boot with JUnit 【发布时间】:2019-01-09 11:04:21 【问题描述】:我想问你几个问题并征求你的意见:
我想测试我的公共方法(我使用 Spring Boot、Mockito、JUnit):
@Service
public class MyClass
public Long getClientId(List<String> nameSurname) throws AuthorizationException
Long operatorId;
if(...)
(... something not interesting ...)
User user = getUserByLogin("AnthonyGates2");
operatorId = nonNull(user) ? user.getOperatorId() : null;
else
List<User> users = getUserListByLogin("AnthinyGates");
operatorId = isNotEmpty(users) ? return operatorId;
return operatorId;
如何测试方法getClientId
?
方法 getUserByLogin
和 getUserListByLogin
在此类 (MyClass
) 中是私有的,但我必须模拟这些私有方法的结果,因为这些方法从外部服务检索数据。
这些私有方法看起来像:
User user = DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);
DelegateImpl.getDelegate().getUserByLogin 从数据库中获取数据,并且必须像这样模拟数据:
when(DelegateImpl.getDelegate().getUserByLogin(any())).thenReturn(user);
如何测试我的公开课?我应该使用 PowerMock/PowerMockito 吗?在我看来,公开这些方法很难看,因为这些方法只在MyClass
中调用。我在 Internet 上找不到适合我的案例的好的教程(Spring Boot、Mockito、JUnit)。
非常感谢您的所有提示!
最好的问候 马修
【问题讨论】:
不要模拟你的私有方法,模拟他们使用的外部服务。 【参考方案1】:仅通过调用公共方法来测试单元。我认为您的示例是服务层中的一个类(包含业务逻辑),两个 getUser... 方法应该在不同的类中(我认为在数据层中),它们可以是公共的。通过构造函数将该类作为依赖项(在服务对象中)注入,以便在测试服务类时模拟它。数据层类(使用 getUser... 方法)也可以通过它自己的单元测试进行测试。
【讨论】:
MyClass 是 MyClassService 并且具有与我的两个私有方法(调用外部服务)非常相似的更多公共单行方法,例如 public void saveUser(User user) DelegateImpl.getDelegate().saveUser(用户); 或者我应该保护这些方法吗?然后使用 Mockito?或者也许最好的选择是将我的私有方法添加到 MyClassService 并将它们更改为 public 并且整个逻辑移动到另一个类,因此在 MyClassService 中应该只有调用外部服务的公共方法...... 如果这两个方法包含业务逻辑,您可以将它们移动到例如 UserService 类,在那里它们可以是公共的,然后您编写一个 UserServiceTest 类。将 UserService 传递给实现 getClientId 的 ClientLoginService(或类似的东西)。如果测试比你的设计不正确(抽象问题和/或静态调用) 是的——多亏了你,我明白当我们不能模拟并且应该测试私有方法时,我们可以重构我们的代码。所以我从 MyClass 中删除了所有逻辑,只留下了调用外部服务的公共方法。我将我的公共方法 getClientId 移动到另一个使用 MyClass 中的公共方法的类。这样我就可以轻松使用 Mockito。谢谢大家的帮助! 很高兴听到重构成功的消息! :)【参考方案2】:如果你不能对一个方法/类进行单元测试,那很可能意味着它做的太多了。尝试将您的私有方法提取到单独的类中。它不需要公开 - 例如,您可以将其本地包放在同一个包中。
稍后,在测试中,您必须注入该类的模拟并模拟其行为。
MyClass 在其单元测试中的设置可能类似于:
AnotherClass anotherClassMock = Mockito.mock(AnotherClass.class);
MyClass myClass = new MyClass(anotherClassMock);
AnotherClass 将有方法 getUserListByLogin 和 getUserByLogin。
编辑:
您的私有方法中的逻辑似乎已经调用了外部类。问题是您通过调用另一个类中的静态getDelegate()
方法来获取对象的实例。
您可以这样做:
在MyClass
中创建一个新字段,该字段的类型与getDelegate()
方法返回的类型相同(我不知道那是什么,我称之为Delegate
)
有 2 个构造函数:一个默认构造函数将 getDelegate
方法的结果分配给您的新字段,另一个将 Delegate
的实例作为参数并将其分配给您的字段
在测试中使用第二个构造函数创建MyClass
的实例并传递Delegate
类的模拟
看起来更像是这样:
class MyClass()
private Delegate delegate;
MyClass()
this.delegate = DelegateImpl.getDelegate();
MyClass(Delegate delegate)
this.delegate = delegate;
// ... the rest
【讨论】:
我的 2 个私有方法非常小,它们看起来像: return DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);仅此而已。 我用另一个命题编辑了我的答案。请,看看它是否有帮助。 不要调用静态方法来解决其他依赖关系(DelegateImpl.getDelegate() 是静态调用)。这使得测试非常困难。通过构造函数使用依赖注入。以上是关于如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法
如何使用 JUnit 测试 Spring Boot aspectj 方法?
如何 JUnit 测试 Spring-Boot 的 Application.java
使用 JUnit 5 的 spring-boot-starter-test