首页 > 解决方案 > Mockito/Spy 不工作,执行原始功能

问题描述

我正在上课:

GP_CategoryService.java 函数 -->

public JSONObject deleteCategory(GP_CategorySubcategoryBean bean) {
        JSONObject data = new JSONObject();
        DirectoryCategoryMaster oCategory = getCategoryMaster(bean);

        if (oCategory.getDirCategoryId() != null) {
            boolean isDeleted = delete(oCategory);
            data.put(ConstantUtil.STATUS, ConstantUtil.SUCCESS);
            data.put(ConstantUtil.DATA, "Category deleted successfully");
        } 
    }

我有 2 个内部函数调用:

  1. getCategoryMaster(bean)
  2. 删除(o类别)

这些基本上是 DAO 调用,直接更新数据库。现在我想单独模拟这两个函数,这样每当我的测试函数运行时,它应该返回 true。

我写了我的测试功能如下:

@Test
    public void deleteCategoryTestDAOV() {
        JSONObject expected = new JSONObject();
        expected.put(ConstantUtil.STATUS, ConstantUtil.SUCCESS);
        expected.put(ConstantUtil.DATA, "Category deleted successfully");
        
        bean.setCategoryId(1);
        bean.setCategoryName("Test");
        DirectoryCategoryMaster master=new DirectoryCategoryMaster();
        master.setDirCategoryId(1);
        GP_CategoryService mock = spy(new GP_CategoryService());
        when(mock.delete(master)).thenReturn(true);
        when(mock.getCategoryMaster(bean)).thenReturn(master);
        JSONObject actual=new JSONObject();
        actual=mock.deleteCategory(bean);
        assertEquals(expected.toJSONString(), actual.toJSONString());   
    }

但是当我运行测试类时,它执行实际的功能,模拟不起作用。有人可以帮我解决这个问题吗?

提前致谢!

标签: javaspringjunitmockitospy

解决方案


Mockito 通过在 mock 下创建类的CGLIB代理来工作。例如,如果你mock(MyClass.class),你会得到一个类的动态代理MyClass$$EnhancedByMockito$$.class,它扩展了MyClass.class。由于代理是 的子类MyClass,因此生成的类允许用户覆盖特定功能(“模拟”它)。大多数方法拦截都通过易于使用的语句为您隐藏在幕后,例如when.

不幸的是,由于 Java 的限制,无法使用本地反射代理内部方法调用。这是因为,如果您直接MyClass:b从实例方法调用实例方法,则插入一个扩展为间接执行MyClass::a的代理类是没有间隙的。MyClassMyClass::b

从技术上讲,可以通过一些非常高级的字节码操作(使用 ByteBuddy 之类的东西)来做到这一点。

不起作用的示例:

class MyClass {
    public String doSomething() {
        return doSomethingElse();
    }
    
    public String doSomethingElse() {
        return "foo";
    }
}

// ...

class MyTest {

    private MyClass myClass;

    // ...
    @Test
    void myTest() {
        when(myClass.doSomethingElse()).thenReturn("Bar");
        assertEquals("bar", myClass.doSomething()); // FAILS
    }
}

使用具有可覆盖方法的中间类,确实有效的示例:

class MyClass {

    private MyClassBackend myClassBackend;

    public MyClass(MyClassBackend myClassBackend) {
        this.myClassBackend = myClassBackend;
    }

    public String doSomething() {
        return myClassBackend.doSomethingElse();
    }
}

class MyClassBackend {
    public String doSomethingElse() {
        return "foo";
    }
}

// ...

class MyTest {

    private MyClass myClass;

    @Mock
    private MyClassBackend myClassBackend;

    @BeforeEach
    void setUp() {
        this.myClass = new MyClass(myClassBackend);
    }

    // ...
    @Test
    void myTest() {
        when(myClassBackend.doSomethingElse()).thenReturn("bar");
        assertEquals("bar", myClass.doSomething()); // PASSES
    }
}

推荐阅读