question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Serializable check is too harsh

See original GitHub issue

Hi, apologies if this has been discussed.

While upgrading my company’s Mockito to 1.10.19, I ran into a lot of this error:

org.mockito.exceptions.base.MockitoException: 
You are using the setting 'withSettings().serializable()' however the type you are trying to mock 'OpportunityClient'
do not implement Serializable AND do not have a no-arg constructor.
This combination is requested, otherwise you will get an 'java.io.InvalidClassException' when the mock will be serialized

In summary, there are two cases triggering this error.

  1. The SubjectUnderTest accepts an object Foo and requires it to be Serializable. And yet Foo doesn’t implement Serializable nor has a default constructor.
  2. Foo itself is Serializable. But the test calls when(foo.createBar()).thenReturn(bar), which behind the scene puts the bar mock onto the state of foo, which requires bar itself to be Serializable. In production, bar isn’t required to be Serializable.

I feel case 1 is possibly reasonable (although it’s still kind of harsh. I’ll get to it in a bit).

Case 2 should not require Bar to be Serializable as it does today. In version 1.9.5, our tests worked around it by using mock(Bar.class, withSettings().serilizable(). But in version 1.10.19, this workaround breaks if Bar has no default constructor.

Possibly the Serializable Proxy Pattern could be used to solve this nicely by stashing the “mock specifications” into a proxy object that can later on deserialize itself back into a mock Bar with the same number of when().thenReturn().

Now let me try to explain why I think even case 1 is too harsh.

Philosophically, I feel that it’s not Mockito’s job to ensure the mocked object works in real life. It’s a mock object after all. It isn’t expected to meet all required specification of the real object.

When in a test I say @Mock(serializable=true) Foo foo;, I’m explicitly asking Mockito to “please pretend my object be Serializable. I don’t want to worry about the real object in this test”. Would the real object be serializable when I pass it into SubjectUnderTest? Well:

  1. There is nothing guaranteeing that production code passes Foo to SubjectUnderTest. It could likely pass a cousin class RealFoo2 that implements Serializable just fine (or not, but the test doesn’t help me with that). It needs to be tested, but IMHO not against a mock foo, especially when Mockito’s when().thenReturn() syntax adds non-existent Serializable requirement to the mock object.
  2. Given the proxy pattern, that Foo doesn’t look like a serializable class isn’t a guarantee that it can’t be. So by throwing this error, Mockito enforces stricter constraint than what’s technically necessary.

I agree that checking basic Serializable requirement in Mockito can catch some bugs. If case 2 can be fixed, it’s likely that case 1 would not have triggered so many false positives.

But with all our workarounds already added because of case 2 and the fair number of our tests triggering this error, the amount of work to fix all of them before we can upgrade Mockito looks daunting.

So, I guess my question is: can we do away with MockCreationValidator.validateSerializable() or make it an opt-in?

Sorry for the long message.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
fluentfuturecommented, Jul 3, 2015

Here’s a real example. The subject under test looks like this:

class SubjectUnderTest implements Serializable {
  private final FooFactory factory;

  SubjectUnderTest(FooFactory factory) {
    this.factory= factory;
  }

  ...
  Foo foo = factory.createFoo();
  ...
}

The testing framework actually tries to serialize SubjectUnderTest so FooFactory needs to be Serializable too.

The test using Mockito looks like this:

Foo foo = mock(Foo.class);
FooFactory mockFactory = mock(FooFactory.class, withSettings().serializable());
when(mockFactory.createFoo()).thenReturn(foo);
SubjectUnderTest subject = new SubjectUnderTest(mockFactory);

But when the framework serializes subject, we get an exception, because the line of when(mockFactory.createFoo()).thenReturn(foo) implicitly adds ‘foo’ as part of mockFactory state.

To work around the problem, our tests had to make foo serializable too:

Foo foo = mock(Foo.class, withSettings().serializable());
...

Again, production code doesn’t need Foo to be Serializable and it may not have a default constructor.

That’s status quo.

Now with v1.10.19, the above work-around breaks, because Foo doesn’t implement Serializable nor has a default constructor.

Back to square one, if I had the choice, I would strongly discourage the code that mocks either FooFactory or Foo. Instead, it should just be a plain old FooFactory subclass that returns Foo. With Java 8, the syntax would actually become more concise than the mockito syntax.

But I can’t go back and fix so many teams’ code. As it stands today, this is the biggest issue blocking the upgrade.

Hopefully I’ve made a clear case.

0reactions
vikkyrkcommented, Jul 30, 2016

Thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to check if an object is serializable in C# - Stack Overflow
The easiest way is to try to serialize the object and catch the exception. (But that's not the cleanest solution). Type.IsSerializable and checking...
Read more >
Serializability vs “Strict” Serializability: The Dirty Secret of ...
In this post, we show how serializability is not and never was a “gold standard” for database systems. In fact, strict serializability has ......
Read more >
How to Prove Serializability - MyDistributed.Systems
The problem with the above definition is that this general notion of serializability is very hard to detect and verify.
Read more >
The redux best practice "Do Not Put Non-Serializable Values ...
This article explains one of the four react-redux best practices: "Do Not Put Non-Serializable Values in State or Actions"
Read more >
"Serializable" classes should have a "serialVersionUID"
The danger in not explicitly choosing the value is that when the class changes, the compiler will generate an entirely new id, and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found