单元测试如何使用 Mockito 模拟存储库

Posted

技术标签:

【中文标题】单元测试如何使用 Mockito 模拟存储库【英文标题】:Unit Tests How to Mock Repository Using Mockito 【发布时间】:2018-12-05 19:11:40 【问题描述】:

我在存根存储库时遇到问题。有人建议我只创建另一个 application.properties(我还没有这样做)并使用像 H2 这样的内存数据库。我想知道我是否可以只存根调用,所以当调用 myDataService.findById(id) 而不是尝试从数据库中获取它时,只能返回一个模拟对象?

一般来说,我是为单元测试和 Spring Boot 编写模拟程序的新手,所以也许我遗漏了一些东西。下面的代码(试图简化并使名称通用,以便在此处发布)。

我的测试课

public class MyServiceImplTest 

    private MyDataService myDataService;
    private NyService myService;
    private MyRepository myRepository;

    @Before
    public void setUp() 
        myDataService = Mockito.mock(MyDataServiceImpl.class);
        myService = new MyServiceImpl(myDataService);
    

    @Test
    public void getById_ValidId() 
        doReturn(MyMockData.getMyObject()).when(myDataService).findById("1");
        when(myService.getById("1")).thenReturn(MyMockData.getMyObject());
        MyObject myObject = myService.getById("1");

        //Whatever asserts need to be done on the object myObject 
    

用于对数据层进行服务调用的类

@Service
public class MyServiceImpl implements MyService 
    MyDataService myDataService;

    @Autowired
    public MyServiceImpl(MyDataService myDataService) 
        this.myDataService = myDataService;
    

    @Override
    public MyObject getById(String id) 
        if(id == null || id == "") 
            throw new InvalidRequestException("Invalid Identifier");
        

        MyObject myObj;
        try 
            myObj = myDataService.findById(id);
        catch(Exception ex) 
            throw new SystemException("Internal Server Error");
        

        return myObj;
    

这是我在测试中遇到问题的地方。当调用 findById() 方法时,变量 repository 为空,因此在尝试执行 repository.findOne(id) 时会抛出异常。这是我试图模拟的,但存储库给我带来了问题。

@Repository
@Qualifier("MyRepo")
public class MyDataServiceImpl 

    @PersistenceContext
    private EntityManager em;

    private MyRepository repository;

    @Autowired
    public MyDataServiceImpl(MyRepository repository) 
        super(repository);
        this.repository = repository;
    

    public MyObject findById(String id) 
        P persitentObject = repository.findOne(id);
        //Calls to map what persitentObject holds to MyObject and returns a MyObject 
    

这里的 MyRepository 代码只是为了显示它是一个扩展 CrudRepository 的空接口

public interface MyRepository extends CrudRepository<MyObjectPO, String>, JpaSpecificationExecutor<MyObjectPO> 


【问题讨论】:

我没有关注你所做的一切,但我认为你希望 myRepository 成为一个模拟对象,而 myDataService 成为一个内部包含模拟存储库的真实对象。如果这就是你想要的,你可以试试myRepository = mock(MyRepository.class); myDataService = new DataServiceImpl(myRepository);。还是我误解了您要做什么? 是的,这正是我想要做的!我想我看了一会儿之后也开始迷惑了。 【参考方案1】:

首先让我说你是在正确的轨道上使用构造函数注入而不是字段注入(这使得使用模拟编写测试变得更加简单)。

public class MyServiceImplTest 

    private MyDataService myDataService;
    private NyService myService;

    @Mock
    private MyRepository myRepository;

    @Before
    public void setUp() 
        MockitoAnnotations.initMocks(this); // this is needed for inititalizytion of mocks, if you use @Mock 
        myDataService = new MyDataServiceImpl(myRepository);
        myService = new MyServiceImpl(myDataService);
    

    @Test
    public void getById_ValidId() 

        doReturn(someMockData).when(myRepository).findOne("1");
        MyObject myObject = myService.getById("1");

        //Whatever asserts need to be done on the object myObject 
    

调用一直从您的服务 --> dataService 进行。但只有您的存储库调用会被模拟。 通过这种方式,您可以控制和测试类的所有其他部分(服务和数据服务)并仅模拟存储库调用。

【讨论】:

非常感谢@pvpkiran!这正是我一直在寻找并且正在努力弄清楚的!一旦我看到解决方案就这么简单! 我不需要使用MockitoAnnotations.initMocks(this) 进行非常相似的测试

以上是关于单元测试如何使用 Mockito 模拟存储库的主要内容,如果未能解决你的问题,请参考以下文章

Mockito单元测试

Android单元测试:Mockito框架的使用

学习笔记单元测试之mockito学习笔记

如何在服务层单元测试中模拟数据库结果?

学习笔记单元测试之mockito学习笔记

学习笔记单元测试之mockito学习笔记