当前位置: 首页 > 知识库问答 >
问题:

使用Spring Security性原则的单元测试方法

谷良弼
2023-03-14

有人可以告诉我我应该如何测试这两种方法吗:

public boolean deleteUser(Principal principal) {
    if (findLoggedInUser(principal) != null) {
        userRepository.delete(findLoggedInUser(principal));
        return true;
    }
    return false;
}

public User findLoggedInUser(Principal principal) {
    return findUserbyUsername(principal.getName());
}

问题是,我正在使用当前登录的用户进行基本身份验证,但不知道如何进行,也不知道我是否可以模仿这些主体。有办法做到这一点吗?那些方法在我的服务层中,所以也许我不能做单元测试,我只能做集成测试,因为那些方法大量使用DB?

编辑1:我更改的测试类:

public class UserServiceBeanTest {

@Spy
@InjectMocks
private UserServiceBean userServiceBean;

@Mock
private UserRepository userRepository;

@Mock
private Principal principal;

@Mock
private PasswordEncoder passwordEncoder;

@Mock
private User userStub;

private String defaultName = "user";
private  String defaultPassword = "password";
private String defaultEmail = "example@example.com";

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
}


@Test
public void shouldReturnTrue_whenUserDeleted() {
    //given
    when(principal.getName()).thenReturn(defaultName);
    when(userServiceBean.findLoggedInUser(principal)).thenReturn(userStub);

    // when
    boolean removed = userServiceBean.deleteUser(principal);

    //then
    assertTrue(removed);
    verify(userRepository, times(1));
}

@Test
public void shouldReturnFalse_whenUserNotFound() {
    //given
    when(principal.getName()).thenReturn(defaultName);
    when(userServiceBean.findLoggedInUser(principal)).thenReturn(null);

    //when
    boolean removed = userServiceBean.deleteUser(principal);

    //then
    assertFalse(removed);
    verify(userRepository, times(0));
}

}

我现在得到这些错误:

org.mockito.exceptions.misusing.UnfinishedVerificationException: 

米辛

g method call for verify(mock) here:
-> at com.doublemc.services.UserServiceBeanTest.shouldReturnTrue_whenUserDeleted(UserServiceBeanTest.java:63)

Example of correct verification:
    verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.


    at com.doublemc.services.UserServiceBeanTest.init(UserServiceBeanTest.java:48)

编辑2:这是我的UserServiceBean类:

package com.doublemc.services;

import com.doublemc.domain.ToDoItem;
import com.doublemc.domain.User;
import com.doublemc.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.security.Principal;

@Service
@Transactional
public class UserServiceBean {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

@Autowired
UserServiceBean(UserRepository userRepository, PasswordEncoder passwordEncoder) {
    this.userRepository = userRepository;
    this.passwordEncoder = passwordEncoder;
}

public User saveUser(User user) {
    User newUser = new User();
    newUser.setUsername(user.getUsername());
    newUser.setEmail(user.getEmail());
    newUser.setPassword(passwordEncoder.encode(user.getPassword()));
    return userRepository.save(newUser);
}

public boolean userExists(User user) {
    return userRepository.findByUsername(user.getUsername()) != null;
}

public Iterable<ToDoItem> getAllToDoItems(User user) {
    return user.getToDoItems();
}

public boolean deleteUser(Principal principal) {
    if (findLoggedInUser(principal) != null) {
        userRepository.delete(findLoggedInUser(principal));
        return true;
    }
    return false;
}

public User findLoggedInUser(Principal principal) {
    return userRepository.findByUsername(principal.getName());
}

}

这是我的用户存储库:

public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
}

编辑6:我为自己创建了另外三个测试:

@Test
public void shouldReturnUser_whenPassedUser() {
    // given
    when(userRepository.save(any(User.class))).thenReturn(new User(defaultName, defaultPassword, defaultEmail));

    // when
    User savedUser = userServiceBean.saveUser(userStub);

    // then
    assertNotNull(savedUser);
    verify(userRepository, times(1)).save(any(User.class));
}

@Test
public void shouldReturnTrue_whenUserExists() {
    // given
    when(userStub.getUsername()).thenReturn(defaultName);
    when(userRepository.findByUsername(userStub.getUsername())).thenReturn(userStub);

    // when
    boolean exists = userServiceBean.userExists(userStub);

    // then
    assertTrue(exists);
    verify(userRepository, times(1)).findByUsername(defaultName);
}

@Test
public void shouldReturnFalse_whenUserNotFoundByUsername() {
    // given
    when(userStub.getUsername()).thenReturn(defaultName);
    when(userRepository.findByUsername(userStub.getUsername())).thenReturn(null);

    // when
    boolean exists = userServiceBean.userExists(userStub);

    // then
    assertFalse(exists);
    verify(userRepository, times(1)).findByUsername(defaultName);
}

以下是经过测试的方法:用户服务:

public User saveUser(User user) {
    User newUser = new User(user.getUsername(), user.getEmail(), passwordEncoder.encode(user.getPassword()));
    return userRepository.save(newUser);
}

UserServiceBean.userExists:

public boolean userExists(User user) {
    return userRepository.findByUsername(user.getUsername()) != null;
}

共有1个答案

汪才
2023-03-14

这就是我要做的(Junit Mockito)。

在给定的例子中有两个测试用例。

顺便说一句,我想你可以做一些重构,因为你(我猜)两次访问数据库:

public boolean deleteUser(Principal principal) {
    User loggedUser = findLoggedInUser(principal);
    if (loggedUser != null) {
        userRepository.delete(loggedUser);
        return true;
    }
    return false;
}

为了测试…

import static org.mockito.Mockito.*;
import mypkg.Service;
import mypkg.User;
import mypkg.UserRepository;
import org.junit.Assert;
import org.junit.Before;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

import java.security.Principal;

public class ServiceTest {

    @Spy
    @InjectMocks
    private Service service;

    @Mock
    private UserRepository userRepository;

    @Mock
    private Principal principal;

    @Mock
    private User userStub;

    private String defaultName = "name";

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

   @org.junit.Test
   public void shouldReturnTrue_whenUserDeleted() throws Exception{
       // Arrange
       when(principal.getName()).thenReturn(defaultName);
       when(service.findUserbyUsername(defaultName)).thenReturn(userStub);

       // Act
       boolean removed = service.deleteUser(principal);

       // Assert
       Assert.assertTrue(removed);
       verify(userRepository, times(1)).delete(userStub);
   }

    @org.junit.Test
    public void shouldReturnFalse_whenUserNotFound() throws Exception{
        // Arrange
        when(principal.getName()).thenReturn(defaultName);
        when(service.findUserbyUsername(defaultName)).thenReturn(null);

        // Act
        boolean removed = service.deleteUser(principal);

        // Assert
        Assert.assertFalse(removed);
        verify(userRepository, times(0)).delete(userStub);
    }
}

要摆脱这种情况的最大好处是,您模拟/存根任何外部依赖项(在本例中为UserRepository),并且仅关注该服务方法中包含的逻辑。删除的内容与测试无关。您所关心的只是该方法是否已使用某个参数调用。就是这样。

如果一切都清楚了,请告诉我……如果需要,我会解释的。

更新

@InjectMocks是一种将依赖项注入到要测试的类中的方便方法。注入由setter/constructor进行,或者作为反射的最后手段。

在上面的示例中,因为Service类具有UserRepositorydependency,并且定义了一个@Mock:

@Mock
private UserRepository userRepository;

Mockito将把它注入到< code >服务中。

@Spy类似于< code>@Mock,只不过它允许您有选择地模仿某些bahvior,并且默认情况下会调用真正的实现。

在我的例子中,我使用它模拟ServicefindUserbyUsernamemethod,因为在我们的两个测试中,内部情况并不重要:

when(service.findUserbyUsername(defaultName)).thenReturn(userStub);
 类似资料:
  • 单元测试 单元测试例子放到自己开发的模块下。 如果依赖了第三方服务端(例如Zookeeper),请手动加入 profile。参考 registry-zookeeper 模块代码。 如果依赖了其它模块要集成测试,请放到 test/test-intergrated 模块中。 如果还依赖了第三方服务端(例如Zookeeper),请放到 test-intergrated-3rd 模块中。 性能测试 关闭了

  • 我正在将Grails2中的一系列单元测试升级到Grails3,并且在使用GORM动态方法的域测试中遇到了问题--特别是addTo{myHasMany}方法。 给定以下域对象 > 我尝试使用DomainClassUnitTestMixin和mockDomain()方法模拟Contact实例,以生成带有GORM方法(或子集)的实例。 我已经尝试了使用基于Spock交互的测试支持的方法。 事实上,我已经

  • 本文向大家介绍SpringSecurity 测试实战,包括了SpringSecurity 测试实战的使用技巧和注意事项,需要的朋友参考一下 引言 试题管理系统的安全模块使用Spring Security,代码从原华软仓库移植,在移植的过程中,发现原测试编写的不好,遂在新系统中对安全模块测试进行了重构。 Spring 测试 添加@SpringBootTest注解,意为这是一个基于SpringBoot

  • 我在单元测试中使用了Moq框架。这是UpdateApplication测试方法: 我在运行测试时遇到一个异常: 我应该如何编写使用Moq更新和删除操作的单元测试?

  • 假设一个名为的类: 和一个类型继承: 从逻辑上讲,必须创建两个单元测试类,每种类型一个。 在单元测试中,我最终会得到: 在单元测试中,我会得到: 在上面的两个测试中,我将测试10秒内的行驶量。 显然,这两种测试都是绿色的。 然而,违反利斯科夫替代原则又如何呢? 事实上,任何客户端代码都应该期望任何短跑运动员在9秒内跑10米。 3个解决方案(前两个解决方案向所有团队的开发人员发出规则信号,即使不是每

  • 问题内容: 我正在通过libgdx编写游戏;我正在使用junit框架来简化代码的单元测试。现在有一部分代码(一个地图生成器,一个将我自己的地图格式转换为TiledMap的类…),我需要对其进行彻底的测试,但是它使用libgdx代码:从文件处理到资产加载。我不打算以这种方式测试实际的图形输出或游戏本身:但是我想测试单个组件(计算,资产访问…),以避免出现明显的错误。 我尝试在“ setUpBefor