Mockito - 创建嵌套的 Mock 对象

11

我正在学习Mockito。在创建嵌套对象的模拟时遇到了问题。请参见

public interface BaseManager {
    public Query createQuery(String queryString);
}

和一个实现该类的实现类

public class BaseManagerImpl implements BaseManager {
    @Autowired
    private SessionFactory sessionFactory;
    // ...
}

模块级别的Hibernate管理器,例如:

public interface RegistrationManager {
    @Transactional
    public List<Country> getCountries();
}

以及该实现类

public class RegistrationManagerImpl implements RegistrationManager {
    @Autowired
    private BaseManager baseManager;
    // ...
}

现在我在创建模拟基础管理器方面遇到了问题。我的测试类是:

现在我正在创建模拟的基础管理器时遇到了问题。我的测试类如下:

 public class MockitoTest {
    private RegistrationManager registrationManager = new RegistrationManagerImpl();

    @Mock private BaseManager baseManager;

    @Mock private SessionFactory sessionFactory;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    //  baseManager.setSessionFactory(sessionFactory);
        registrationManager.setBaseManager(baseManager);
    }
    // ...
    // @Test
}

问题: baseManager内未实例化sessionFactory。 请帮忙创建模拟对象。谢谢!


1
你的意思是,“baseManager内部出现了NullPointerException”?你已经对baseManager对象进行了模拟 -- 它不应执行任何未存根的代码。在这个例子中,您还没有显示更多的内容;请发布实际的JUnit测试和完整的堆栈跟踪。 - Thorn G
3个回答

5
问题在于你正在创建 BaseManager 的模拟,但只有 BaseManagerImpl 有一个 SessionFactory 字段。Mockito 不知道 BaseManagerImpl。在你的代码中,你创建了两个完全独立的模拟。
单元测试是关于测试一个单元的。所以你应该分别测试 BaseManagerImplRegistrationManagerImpl
所以你先测试 BaseManagerImpl:
public class BaseManagerImplTest {

    private BaseManagerImpl target;

    // ...
}

然后你需要测试RegistrationManagerImpl

public class RegistrationManagerImplTest {

    private RegistrationManagerImpl target;

    // ...
}

我建议您在测试类中为测试目标使用名称target或类似的名称,因为这样可以使您的代码更易于阅读。
另外一件事:如果您要测试一个对象,那么它的所有依赖关系都应该被mock,但您不必担心mocks的依赖关系。您只需模拟它们的方法调用,例如:
Mockito.when(myMock.someMethod()).thenReturn(someResultObject);

你说得对。我们不应该担心模拟的依赖关系。但是我有一个问题,就是:我有一个服务 ServiceA.methodA(),它调用了 ServiceB.methodB(..),而 methodB(...) 又调用了 ManagerC.serviceC(...)。在这种情况下,我只想模拟 ManagerC.serviceC(...),而对于 ServiceB.methodB(..),我希望进行真实调用,因为我不想模拟我的业务层。在这种情况下,我和那个提问者一样遇到了同样的问题。我该如何解决? - Ketan
我不理解这个。你所说的“real call”是什么意思? - Adam Arold
我指的是从服务(A.a1)中,我正在调用同一类别的另一个服务(A.a2),它反过来又调用另一个类别的方法(B.b1),我只想模拟这个(B.b1)方法,有可能吗? - Ketan

4

在要测试的类之前加上@InjectMocks注解,并模拟被basemanger或sessionFactory调用的方法。

public class MockitoTest {
    @InjectMocks
    private RegistrationManager registrationManager = new RegistrationManagerImpl();

    @Mock private BaseManager baseManager;

    @Mock private SessionFactory sessionFactory;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    //  baseManager.setSessionFactory(sessionFactory);
        registrationManager.setBaseManager(baseManager);

        Mockito.when(baseManager.yourMethod()).thenReturn(someObject);
    }
    // ...
    // @Test
}

希望这正是你要找的内容!

那不是问题。检查我的答案。 - Adam Arold

2

你不能将SessionFactory的mock注入到BaseManager的mock中。

在测试RegistrationManagerImpl时,只需要有一个BaseManager的mock即可。您可以使用方法存根来使BaseManager的方法在从RegistrationManagerImpl方法被调用时返回存根值。所以,如果您有一个如下所示的RegistrationManagerImpl

public class RegistrationManagerImpl implements RegistrationManager {
    @Autowired
    private BaseManager baseManager;
    // ...
    public String doSomething(){
         return baseManager.process();  
    }
}

你可以这样编写与MockitoTest相关的代码:
public class MockitoTest {
    @InjectMocks
    private RegistrationManager registrationManager = new RegistrationManagerImpl();

    @Mock private BaseManager baseManager;

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

    }
    // ...
    @Test
    public void test() {
    when(baseManager.process()).thenReturn("hello");

    assertEquals("hello", registrationManager.doSomething());
    }
}

在测试BaseManager时,你需要使用SessionFactorymock


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接