You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@druid.apache.org by Suneet Saldanha <su...@imply.io> on 2020/02/02 21:13:34 UTC

mocking frameworks for tests in druid

Hi devs,

I've been writing tests in druid for a little bit now and wanted to hear
your thoughts on using EasyMock as the mocking framework.

I've found it pretty tedious to write tests using easy mock because it's
very verbose, and requires me to do a lot of set up for the mock objects.
Because of this setup work, the mocks tend to be very tightly coupled with
the implementation which makes it harder to refactor in the future. The
extra stubbing work also discourages me from adding more tests. *IMO, this
makes it harder for us to write well tested code.*

TL;DR at the bottom with questions for those on this list

I've used Mockito in the past, and find it to be more fluent when writing
tests. See this example for a side by side comparison.

*Using Mockito* -  create a mock object that returns mock objects for every
function call.

@Mock(answer = Answers.RETURNS_MOCKS)
private IndexTask.IndexIngestionSpec ingestionSpec;

*Using EasyMock*

@Mock
private IndexTask.IndexIngestionSpec ingestionSpec;

@Mock
private DataSchema dataSchema;

@Mock
private IndexTask.IndexIOConfig indexIOConfig;

@Mock
private IndexTask.IndexIOConfig indexTuningConfig;

@Before
public void setUp() throws IOException
{

  EasyMock.expect(ingestionSpec.getDataSchema())andStubReturn(dataSchema);

  EasyMock.expect(ingestionSpec.getIndexIOConfig())andStubReturn(indexIOConfig);

  EasyMock.expect(ingestionSpec.getIndexTuningConfig())andStubReturn(indexTuningConfig);

  EasyMock.replay(ingestionSpec);

  ...

Now if the interface of ingestionSpec changes to add a new method, we need
to stub that in the tests as well. If getDataSchema() is called an extra
time the mock throws an exception because it's not expected that the
function will be called twice
IMO this makes them brittle because they are tightly coupled to the code.

With Mockito, you only need to mock objects that you want to interact with
in the tests.

@Mock(answer = Answers.RETURNS_MOCKS)
private IndexTask.IndexIngestionSpec ingestionSpec;

@Mock(answer = Answers.RETURNS_MOCKS)
private DataSchema dataSchema;

@Before
public void setUp() throws IOException
{

  Mockito.when(ingestionSpec.getDataSchema()).thenReturn(dataSchema);

  Mockito.when(dataSchema.getDimensionsSpec()).thenReturn(dimensionsSpec);

  ...

Another feature of Mockito I like is that it allows you to spy real
objects. So if you have a class that creates a File, you can spy the
function and give it a fake File to operate on instead of having your tests
have to create real files. For example, below is some test code that
doesn't get a real InputSourceReader which means the test doesn't need to
add any set up code to get the reader. It simply says, if you need to use
an inputSourceReader - use this one.

target = Mockito.spy(new IndexedTableSupplier(INDEX_KEYS,
ingestionSpec, () -> tmpDir));
Mockito.doReturn(inputSourceReader).when(target).getInputSourceReader(dataSchema,
tmpDir);

Here's a list of similarities/ differences between the frameworks -
https://github.com/mockito/mockito/wiki/Mockito-vs-EasyMock
Here's a stackoverflow post asking about maintainability in large projects
-
https://stackoverflow.com/questions/2864796/easymock-vs-mockito-design-vs-maintainability

*TL;DR: *Am I using EasyMock incorrectly? Do these features exist and I
just don't know how to use them?

What would it take for us to try another framework like Mockito?

Re: mocking frameworks for tests in druid

Posted by Gian Merlino <gi...@apache.org>.
I've never really liked EasyMock. I agree that its design tends to make
test code too tightly coupled with the specific implementation being tested.

> What would it take for us to try another framework like Mockito?

IMO, for me all it'd take is a PR changing one of our EasyMock tests to a
Mockito test and showing that it becomes nicer.

On Sun, Feb 2, 2020 at 1:13 PM Suneet Saldanha <su...@imply.io>
wrote:

> Hi devs,
>
> I've been writing tests in druid for a little bit now and wanted to hear
> your thoughts on using EasyMock as the mocking framework.
>
> I've found it pretty tedious to write tests using easy mock because it's
> very verbose, and requires me to do a lot of set up for the mock objects.
> Because of this setup work, the mocks tend to be very tightly coupled with
> the implementation which makes it harder to refactor in the future. The
> extra stubbing work also discourages me from adding more tests. *IMO, this
> makes it harder for us to write well tested code.*
>
> TL;DR at the bottom with questions for those on this list
>
> I've used Mockito in the past, and find it to be more fluent when writing
> tests. See this example for a side by side comparison.
>
> *Using Mockito* -  create a mock object that returns mock objects for every
> function call.
>
> @Mock(answer = Answers.RETURNS_MOCKS)
> private IndexTask.IndexIngestionSpec ingestionSpec;
>
> *Using EasyMock*
>
> @Mock
> private IndexTask.IndexIngestionSpec ingestionSpec;
>
> @Mock
> private DataSchema dataSchema;
>
> @Mock
> private IndexTask.IndexIOConfig indexIOConfig;
>
> @Mock
> private IndexTask.IndexIOConfig indexTuningConfig;
>
> @Before
> public void setUp() throws IOException
> {
>
>   EasyMock.expect(ingestionSpec.getDataSchema())andStubReturn(dataSchema);
>
>
> EasyMock.expect(ingestionSpec.getIndexIOConfig())andStubReturn(indexIOConfig);
>
>
> EasyMock.expect(ingestionSpec.getIndexTuningConfig())andStubReturn(indexTuningConfig);
>
>   EasyMock.replay(ingestionSpec);
>
>   ...
>
> Now if the interface of ingestionSpec changes to add a new method, we need
> to stub that in the tests as well. If getDataSchema() is called an extra
> time the mock throws an exception because it's not expected that the
> function will be called twice
> IMO this makes them brittle because they are tightly coupled to the code.
>
> With Mockito, you only need to mock objects that you want to interact with
> in the tests.
>
> @Mock(answer = Answers.RETURNS_MOCKS)
> private IndexTask.IndexIngestionSpec ingestionSpec;
>
> @Mock(answer = Answers.RETURNS_MOCKS)
> private DataSchema dataSchema;
>
> @Before
> public void setUp() throws IOException
> {
>
>   Mockito.when(ingestionSpec.getDataSchema()).thenReturn(dataSchema);
>
>   Mockito.when(dataSchema.getDimensionsSpec()).thenReturn(dimensionsSpec);
>
>   ...
>
> Another feature of Mockito I like is that it allows you to spy real
> objects. So if you have a class that creates a File, you can spy the
> function and give it a fake File to operate on instead of having your tests
> have to create real files. For example, below is some test code that
> doesn't get a real InputSourceReader which means the test doesn't need to
> add any set up code to get the reader. It simply says, if you need to use
> an inputSourceReader - use this one.
>
> target = Mockito.spy(new IndexedTableSupplier(INDEX_KEYS,
> ingestionSpec, () -> tmpDir));
>
> Mockito.doReturn(inputSourceReader).when(target).getInputSourceReader(dataSchema,
> tmpDir);
>
> Here's a list of similarities/ differences between the frameworks -
> https://github.com/mockito/mockito/wiki/Mockito-vs-EasyMock
> Here's a stackoverflow post asking about maintainability in large projects
> -
>
> https://stackoverflow.com/questions/2864796/easymock-vs-mockito-design-vs-maintainability
>
> *TL;DR: *Am I using EasyMock incorrectly? Do these features exist and I
> just don't know how to use them?
>
> What would it take for us to try another framework like Mockito?
>