Different method calls returning different values

1,529 views
Skip to first unread message

Gili

unread,
Oct 27, 2008, 1:12:49 PM10/27/08
to mockito
Hi,

I'd like to mock the following method calls:

Query query = EntityManager.createQuery("queryString");
query.setParameter(key, value);
Object result = query.getSingleResult();

I want to tell Mockito to return different results depending upon what
parameters were passed in, but there is only a single Mock object to
play with. To reiterate, the query string is the same, but the
parameters differ.

Any ideas?

Gili

Bartosz Bańkowski

unread,
Oct 27, 2008, 1:19:52 PM10/27/08
to moc...@googlegroups.com
Hi Gili,

> Query query = EntityManager.createQuery("queryString");
> query.setParameter(key, value);
> Object result = query.getSingleResult();
>
> I want to tell Mockito to return different results depending upon what
> parameters were passed in, but there is only a single Mock object to
> play with. To reiterate, the query string is the same, but the
> parameters differ.
>
> Any ideas?

Could you show more source code? In the given example I'd not care
about query.getSingleResult, I'd rather focus on query.setParameter
and would verify it with different arguments.

Best regards,
Bartosz

Gili Tzabari

unread,
Oct 27, 2008, 2:01:10 PM10/27/08
to moc...@googlegroups.com

Actually I was thinking the opposite. For example, consider the following.

Common codebase:

public void MyQuery(User user)
{
Query query = EntityManager.createQuery("select users from users
where users.username=:username and users.age=:age");
query.setParameter("username", user.getUsername());
query.setParameter("age", user.getAge());
return query.getSingleResult();
}

First run:

MyQuery(new User("joe", 15));
MyQuery(new User("joe", 18));

I did two things on purpose to make this query more difficult to mock:

1) username and age is hidden behind a User object. I could mock
MyQuery() altogether but in this case I'm trying to mock at a lower level.

2) I picked overlapping parameter values in order to demonstrate that
just because some parameter values are the same doesn't mean the
end-result should be the same (*all* parameters must match up).

I was thinking that the way you could implement this in Mockito is by
accumulate information every time setParameter() is invoked. Basically
Entity.createQuery() would return a mock(Query) and that mock would have
a Map<String, Object> associated with it. setParameter() would push data
into that Map and when query.getSingleResult() gets invoked you'd read
the Map and based on its values give back a different result. Invoking
getSingleResult() would also clear() the map so it's ready for reuse the
next time a mock query is invoked.

It's not clear to me is how to tell a mock object to store or retrieve
arbitrary user data. I could store the Map outside the mock object but
it makes the code a lot uglier.

I *think* you could do it the way I've outlined above. I haven't
actually tried it yet.

Gili

Bartosz Bańkowski

unread,
Oct 27, 2008, 3:19:18 PM10/27/08
to moc...@googlegroups.com
I'm a bit confused. What actually do you want to test? I'm not sure if
I understand you correctly.

If all you want to test is MyQuery method here is a piece of code for this case:

EntityManager entityManager = Mockito.mock(EntityManager.class);
Query query = Mockito.mock(Query.class);
Mockito.when(entityManager.createQuery("select users from


users where users.username=:username and

users.age=:age")).thenReturn(query);


MyQuery(new User("joe", 15));

Mockito.verify(query).setParameter("username", "joe");
Mockito.verify(query).setParameter("age", 15);

As you can see I'm verifying setParameter method only. There is no
need to verify the result of getSingleResult, so the functionality
with Map that you described is not necessary.

Best regards,
Bartosz

Gili Tzabari

unread,
Oct 27, 2008, 6:59:12 PM10/27/08
to moc...@googlegroups.com
Hi Barosz,

I am testing code which, in turn, depends upon MyQuery. I need to stub
getSingleResult() because the higher-level code depends upon its result.

Hopefully this example will be clearer:

- class User contains two fields: username and password
- the database contains two rows:

"john", "password1"
"sarah", "password2"

- I am trying to test setUser(String username, User user). This method
updates the database state associated with "username".

So for example, if someone invokes setUser("john", new User("david",
"password3")) then what happens is that the database will look up "john"
and change the row values to "david" and "password3".

- If you try to renaming "john" to "sarah" the method should throw an
exception because the username is already taken.

Now, this is where MyQuery comes in. Let's redefine MyQuery as follows:

public User getUser(String username)
{
Query query = EntityManager.createQuery("select users from users
where users.username=:username");
query.setParameter("username", username);
return query.getSingleResult();
}

In order to implement setUser("john", new User("david", "password3"))
the underlying algorithm needs to do two lookups. One invocation looks
up "john", another invocation looks up "david" (to avoid collisions). I
want to stub Query such that it will return the correct User object
depending on the parameter passed in.

Gili

Gili

unread,
Oct 27, 2008, 7:19:05 PM10/27/08
to mockito
I think have found a much simpler solution.

I was trying to test the application layer which invokes methods in
the DAO layer which invokes methods in the database layer. My original
plan was to stub out the database layer and test both the application
and DAO layers at the same time.

Instead, I will stub out the DAO layer, test the application layer on
its own. Then when I get around to testing the DAO layer I no longer
need to worry about multiple calls being made to the same Query
object. Instead, each test will invoke at most one DAO method and test
one interaction with the Query object.

I believe this will simplify both of our lives ;) As an added bonus, I
believe this design will have the same code coverage (and catch as
many bugs) as the original design. Am I wrong?

Gili

szczepiq

unread,
Oct 28, 2008, 5:21:33 AM10/28/08
to moc...@googlegroups.com
Hi,

>Instead, I will stub out the DAO layer, test the application layer on
>its own.

This what I would do either.

>each test will invoke at most one DAO method and test
>one interaction with the Query object.

This is what I wouldn't do, at least not with mocks :) I would rather
test DAO layer functionally by talking to the database.

Cheers,
Szczepan Faber

Reply all
Reply to author
Forward
0 new messages