dart-lang / dart-lang/language

Better mockability

Open
#3,585 7 comments 0 reactions 0 assignees View on GitHub
request
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

I would like to bring up an issue with the rather limiting testability infrastructure. Presently there are mock and fake and they do an amazing job in most common scenarios. However for just testing a superclass logic I believe there are shortcomings. In specific there seems no way have create mocks that mix and match stubs and actual implementation details which simplifies testing code for superclasses. My understanding is that constraints are partly driven by lacking/limited support of language and runtime features required.

Consider the following example a rather simplified state machine:

```
class Parser
{
void uknown() {}
void data() {}
void scan( List bytes ) {
....
}
}
```

In csharp I can easilly verify wether the implementation of my parser is correct, because language/runtime infrastructure allow me to mix implementation of superclass together with stubbed methods provided from the mock.

```
Mock mock = new Mock(behavior: MockBehavior.Strict);
mock.Setup(s => s.scan(It.IsAny>())).CallBase();
mock.Object.scan(new List { 27, 36, 0, 0, 28 });
mock.Verify(s => s.uknown(), Times.Never);
mock.Verify(s => s.data(), Times.AtLeastOnce);
```

In dart the mock and fake libraries have no equivelent. It seems to rely on overriding 'noSuchMethod' to record invocations of unimplemented methods to verify and assert. This works great in scenarios where dependency injection is possible or the status quo doesn't work well outside those scenarios. Language constructs like 'MockParser extends Mock implements Parser' inheritly discard implementation details of the superclass, and replace all of them with the 'noSuchMethod' including the scan method we actually trying to verify.

You have a couple of options:

You can manually create classes for special testing purposes by extending from the super class but that effort would be orthogonal to your testing needs. However it is rarely a considered good because this is a factually more verbose and require more effort to maintain in order to keep in sync with the implementation.

```
class TestingParser extends Parser
{
void uknown() { uknownCalled++; }
void data() { dataCalled++; }
void scan( List bytes ) {
....
}
}
```

You can refactor the implementation code so you introduce dependecy injection but this actually trades off simplicity in your testing code for complexity in your implementation othogonal to your testing needs.

```
class ParserObserver
{
void uknown() {}
void data() {}
}

class Parser
{
void scan( List bytes , ParserObserver ) {
....
}
}

class MockParserObsever extends Mock implements ParserObserver { }

```

None of these options are particulairly appealing because both scenarios trade of the simplicity versus complexity, ideally you just simply want have a simple implementation and matching simple testing code to verify the behavior. Would it be possible to introduce more runtime/language features improve testing of these type of scenarios?

Contributor guide

Open the contributing guide

Research direction

The issue names no repository files, tests, or concrete language change. Start by reviewing the described mock, fake, and noSuchMethod limitations and the superclass-mocking examples; done would require an agreed Dart language or runtime proposal for partial superclass implementations and verification.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.