Junit4 Mockito UnfinishedVerificationException TransactionManager

Я просто столкнулся со сложной проблемой, когда начал модульное тестирование некоторых методов контроллера в Java-приложении на основе Spring FW4.

Мой ApplicationConfig.java аннотирован с помощью @Configuration и @EnableTransactionManagement(proxyTargetClass = true) и общедоступного метода контроллера, который я создал для сохранения нового объекта простого класса сущностей, который тестируется со следующим ControllerTestClass

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:/test-context.xml"})
@TransactionConfiguration(defaultRollback = true, transactionManager = "annotationDrivenTransactionManager")
public class TestController 
    @Autowired
    public MyClassService myClassServiceMock;

    protected MockMvc mockMvc;

    @Autowired
    protected WebApplicationContext webApplicationContext;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    };

    @org.junit.Before
    public void reset() {
        Mockito.reset(myClassServiceMock);
    }

    @org.junit.After
    public void after() { 
         verifyNoMoreInteractions(myClassServiceMock);
    }


    @Test
    public void testSaveObject() throws Exception {
        MyObject object = new MyObjectBuilder().withName("object").withDate("2014-08-15").build();
        when(myClassServiceMock.createObject(objectName, objectDate)).thenReturn(object);

        [.. mockMvcTest which works ... ]

        verify(myclassServiceMock, times(1)).createObject(objectName, objectDate);
    }
}

В следующей части debug.log я не могу понять причину проблемы, но когда я удаляю аннотацию @EnableTransactionManager, ошибки не возникает...

2014-08-15_17:25:59.608 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [a.b.c.MyClassService$$EnhancerByMockitoWithCGLIB$$cf62a86c.saveObject]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2014-08-15_17:25:59.608 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@513f39c] for JPA transaction
2014-08-15_17:25:59.616 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@320cac01]
2014-08-15_17:25:59.618 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
2014-08-15_17:25:59.618 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@513f39c]
2014-08-15_17:25:59.633 [main] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@513f39c] after transaction
2014-08-15_17:25:59.633 [main] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
2014-08-15_17:25:59.635 [main] DEBUG o.s.t.c.s.DirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@8f72029 testClass = MyControllerTest, testInstance = a.b.c.MyControllerTest@453204e6, testMethod = testSaveObject@MyClassControllerTest, testException = org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at a.b.c.MyClassService$$FastClassBySpringCGLIB$$809f2bf.invoke(<generated>)

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

Я был бы очень признателен за помощь, потому что мой странник и исследование уже продолжаются пару дней :(

Спасибо


person chuckice    schedule 15.08.2014    source источник


Ответы (1)


Во-первых, вы не инициализируете Mockito в своих тестах. Читать этот пост

Вам нужно вызвать MockitoAnnotations.initMocks(), потому что вы уже используете @RunWith(SpringJUnit4ClassRunner.class) и можете указать только одного бегуна в классе.

@Before
public void reset() {
    MockitoAnnotations.initMocks(this);
    // Mockito.reset(myClassServiceMock);   <= remove this line
}

Я думаю, вы также хотите использовать @Mock вместо @Autowired для этого макета, чтобы у вас был экземпляр макета Mockito, который вы могли бы затем вызвать verify() позже. Вам также нужно будет ввести myClassServiceMock в тестируемый класс (т.е. контроллер)

@Mock
public MyClassService myClassServiceMock;

Вы можете удалить вызов Mockito.reset(), так как @Mock создаст новый экземпляр для каждого метода тестирования.

Если вы намеревались использовать @Autowired и получить экземпляр MyClassService из контекста вашего приложения, вы не сможете вызывать для него какие-либо методы Mockito, такие как verify().

Я также ожидаю, что @TransactionConfiguration не требуется, потому что вы никогда не попадаете в свою базу данных (потому что вы издеваетесь над своим сервисным уровнем), поэтому вы можете удалить его. Если вы нажимаете на свою базу данных в своем тесте, то это другая история, но я не могу сказать этого по предоставленному вами коду.

person Brad    schedule 15.08.2014
comment
Это очень помогает мне, мой друг! Благодарю вас! Я большой новичок в JUnit, и до сих пор я работал с отличными разработчиками тестов. Теперь моя очередь набираться опыта. Я получил ваши оценки и понял из документации Mockito, почему это делает мою жизнь проще. Но позвольте мне спросить, что вы подразумеваете под: вам также придется внедрить myClassServiceMock в тестируемый класс (то есть в контроллер)? Работаете с @Inject? - person chuckice; 16.08.2014
comment
Предполагая, что класс контроллера, который вы тестируете, называется MyController, тогда я ожидаю, что ваш тест внедрит myClassServiceMock, созданный @Mock. Как MyController получает ссылку на MyClassService? Запрашивает ли он его напрямую из контекста приложения или это @Autowired? - person Brad; 17.08.2014
comment
MockitoAnnotations.initMocks() Эта строка не инициализирует mockito, она просто находит все аннотации и вставляет имитированный объект во все поля @Mock. Вы можете использовать Mockito.mock() вместо аннотаций - person Geniy; 14.06.2016