JMock触发AssertionError:预期调用一次,从未调用 - 但它已被调用
我对 java 编程还很陌生,但我尝试直接从单元测试开始,因此也使用了 JMock。我已经实现了一些有效的测试用例(使用 JMock),但是我无法运行这个测试用例。
我做了什么: 我编写了一个测试类,它创建一个模拟对象,然后我期望一个(使用 oneOf)调用。运行单元测试后,它说它失败了(但日志说不然,因为我使用 will(returnValue(x))
打印出调用时返回的数据。
下一个有趣/奇怪的事情是- 如果我将 oneOf 更改为“从不”,单元测试会成功,但会抛出异常:
Exception in thread "Thread-2" java.lang.AssertionError: unexpected invocation: blockingQueue.take()
期望: 预期永远不会,永远不会调用:blockingQueue.take();回报 在此之前发生了什么:什么都没有!
这里是代码:
@RunWith(JMock.class)
public class ExecuteGameRunnableTest {
private Mockery context = new JUnit4Mockery();
private Thread testObject;
private BlockingQueue<Game> queueMock;
private Executor executorMock;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
queueMock = context.mock(BlockingQueue.class);
executorMock = context.mock(Executor.class);
testObject = new Thread(new ExecuteGameRunnable(queueMock, executorMock, true));
}
@After
public void tearDown() {
queueMock = null;
executorMock = null;
testObject = null;
}
@Test
public void testQueueTake() throws InterruptedException {
final Game game = new Game();
game.setId(1);
game.setProcessing(false);
context.checking(new Expectations() {{
never(queueMock).take(); will(returnValue(game));
}});
testObject.start();
context.assertIsSatisfied();
}
}
以及我正在测试的可运行程序:
public class ExecuteGameRunnable implements Runnable {
private BlockingQueue<Game> queue;
private Executor executor;
private Boolean unitTesting = false;
static Logger logger = Logger.getLogger(ExecuteGameRunnable.class);
public ExecuteGameRunnable(BlockingQueue<Game> queue, Executor executor) {
this.queue = queue;
this.executor = executor;
}
public ExecuteGameRunnable (BlockingQueue<Game> queue, Executor executor, Boolean unitTesting) {
this(queue,executor);
this.unitTesting = unitTesting;
}
public void run() {
try {
do {
if (Thread.interrupted()) throw new InterruptedException();
Game game = queue.take();
logger.info("Game "+game.getId()+" taken. Checking if it is processing"); // THIS ONE PRINTS OUT THE GAME ID THAT I RETURN WITH JMOCK-FRAMEWORK
if (game.isProcessing()) {
continue;
}
game.updateProcessing(true);
executor.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
} while (!unitTesting);
} catch (InterruptedException ex) {
logger.info("Game-Execution-Executor interrupted.");
return;
} catch (DataSourceException ex) {
logger.fatal("Unable to connect to DB whilst executing game: "+id_game,ex);
return;
}
}
}
I'm pretty new to programming with java but I've tried to directly start with unit-testing and therefore also used JMock. I have already implemented some test-cases (with JMock) that work, but this one I just can't get to run.
What I did:
I wrote a test-class which creates a mock object and then I'm expectation one (using oneOf) invocation. After running the unit test it says it fails (but the logs say otherwise, as i print out the data I returned at the invocation using will(returnValue(x))
.
The next funny/weird thing is - if I change the oneOf to "never" the unit test succeeds, but it throws an Exception:
Exception in thread "Thread-2" java.lang.AssertionError: unexpected invocation: blockingQueue.take()
expectations:
expected never, never invoked: blockingQueue.take(); returns
what happened before this: nothing!
Here the code:
@RunWith(JMock.class)
public class ExecuteGameRunnableTest {
private Mockery context = new JUnit4Mockery();
private Thread testObject;
private BlockingQueue<Game> queueMock;
private Executor executorMock;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
queueMock = context.mock(BlockingQueue.class);
executorMock = context.mock(Executor.class);
testObject = new Thread(new ExecuteGameRunnable(queueMock, executorMock, true));
}
@After
public void tearDown() {
queueMock = null;
executorMock = null;
testObject = null;
}
@Test
public void testQueueTake() throws InterruptedException {
final Game game = new Game();
game.setId(1);
game.setProcessing(false);
context.checking(new Expectations() {{
never(queueMock).take(); will(returnValue(game));
}});
testObject.start();
context.assertIsSatisfied();
}
}
and the runnable that I'm testing:
public class ExecuteGameRunnable implements Runnable {
private BlockingQueue<Game> queue;
private Executor executor;
private Boolean unitTesting = false;
static Logger logger = Logger.getLogger(ExecuteGameRunnable.class);
public ExecuteGameRunnable(BlockingQueue<Game> queue, Executor executor) {
this.queue = queue;
this.executor = executor;
}
public ExecuteGameRunnable (BlockingQueue<Game> queue, Executor executor, Boolean unitTesting) {
this(queue,executor);
this.unitTesting = unitTesting;
}
public void run() {
try {
do {
if (Thread.interrupted()) throw new InterruptedException();
Game game = queue.take();
logger.info("Game "+game.getId()+" taken. Checking if it is processing"); // THIS ONE PRINTS OUT THE GAME ID THAT I RETURN WITH JMOCK-FRAMEWORK
if (game.isProcessing()) {
continue;
}
game.updateProcessing(true);
executor.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
} while (!unitTesting);
} catch (InterruptedException ex) {
logger.info("Game-Execution-Executor interrupted.");
return;
} catch (DataSourceException ex) {
logger.fatal("Unable to connect to DB whilst executing game: "+id_game,ex);
return;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
JMock 不是线程安全的。它旨在支持单元测试,而不是非常小的集成测试。坦率地说,在这种情况下,我会使用真正的 BlockingQueue 而不是模拟的。并且您的生产代码中不应该有
unitTesting
标志。另一件事是,您不需要将测试类中的字段设置为 null,jUnit 会为每个测试刷新实例。
JMock isn't thread safe. It's intended to support unit testing, rather than what is a very small integration test. Frankly, in this case I'd use a real
BlockingQueue
rather than a mock one. And there is no way you should have aunitTesting
flag in your production code.One more thing, you don't need to set the fields in the test class to null, jUnit flushes the instance for every test.