Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace hard final with soft, annotation-based @final #57

Closed
wants to merge 1 commit into from
Closed

Replace hard final with soft, annotation-based @final #57

wants to merge 1 commit into from

Conversation

ostrolucky
Copy link
Contributor

I need to depend on ResourceOutputStream, because I rely on methods missing from interface. And I need to mock it for my tests. Hence I am proposing to drop hard final and replace it with annotation based final, which is same strategy Symfony follows. This gives you best from both worlds - you are still not obligated to keep an eye for inheritance based BC issues, but now people can mock it

This is required to be able to mock the Stream when depending on implementation - that happens when requiring methods outside the InputStreamInterfacd
Copy link
Member

@kelunik kelunik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like removing the final. You mentioned the reason why you can't rely on the interfaces, but why do you need to mock the instances?

@ostrolucky
Copy link
Contributor Author

Well would you be more comfortable with adding new interface for this? Eg. ResourceAwareStream?

Not sure I understand your question. I need to mock instance because that's what class constructor requires. It can't require InputStream interface because method I need is not there.

@kelunik
Copy link
Member

kelunik commented May 21, 2019

What does ResourceAwareStream solve? Which methods do you need?

Instead of mocking the InputStream, you could use a socket pair, no?

@ostrolucky
Copy link
Contributor Author

I need getResource method.

My tests use code like

$output
    ->expects($this->exactly(1))
    ->method('write')
    ->willReturn(new Success())
;

so expecting raw resource is not going to cut it.

@kelunik
Copy link
Member

kelunik commented May 21, 2019

I think this is a rather bad test, because it tests implementation details. Instead, I suggest testing the behavior, so e.g.

public function testSomething() {
    [$a, $b] = Socket\pair();

    whenSomethingHappens(new ResourceOutputStream($a));

    thenWrittenStreamContentIs("foobar", new ResourceInputStream($b));
}

public function whenSomethingHappens(ResourceOutputStream $stream) {
    // your implementation here

    $stream->close(); // ensure the stream is closed, so buffer doesn't hang
}

public function thenWrittenStreamContentIs(
    string $content,
    ResourceInputStream $stream
) {
    $this->assertSame($content, ByteStream\buffer($stream));
}

@ostrolucky
Copy link
Contributor Author

Well it's unit test, it's what it's meant to test. I have integration tests for other things. I need to test precisely write is triggered once only and not more. This is in order to make sure number of read and write calls is not getting out of sync. Getting same end result with different number of these calls than expected would be wrong.

@trowski
Copy link
Member

trowski commented Jul 25, 2019

@ostrolucky I would do as @kelunik recommended and use a socket pair to test what is written to the socket is what is expected. If you need the resource, I would also consider passing the resource to the constructor instead of ResourceOutputStream.

@trowski trowski closed this Jul 25, 2019
@ostrolucky
Copy link
Contributor Author

I am simulating behaviour that happens when write method returns NULL, that's impossible to emulate with socket pair. You did not present any reason keeping hard final.

@trowski
Copy link
Member

trowski commented Jul 25, 2019

The write method should never return null.

These classes all implement an interface. There's no reason to remove final from them – you can always write another class that implements the interface, probably wrapping one of these existing classes.

@ostrolucky
Copy link
Contributor Author

Sorry, I meant 0. Yes, I can create wrapper, but why am I forced to?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants