8 ms·
Do you consider test mocks to be a second implementation that warrants the interface?
by oldmanhorton 7y ago
Do you consider test mocks to be a second implementation that warrants the interface?
- ses1984 7y agoThere are test frameworks that obviate the need to do this.
- gautamdivgi 7y agoNot if you use something like c/c++. There are test frameworks but there is no way from preventing testing code from leeching into functional code. But yes - Java/python, etc. you shouldn't need to do this.
- taco_emoji 7y agoNot on all platforms. In .NET you need an interface or virtual method in order to allow the mocking framework to dynamically generate a concrete subclass that mocks out that method's functionality.
- CraigJPerry 7y agoWhat’s cheaper to own though - a whole test library / framework (assuming you don’t use any other feature), or a simple interface? I’m generally hesitant to add test libraries if there’s an easy design choice alternative that keeps the code simpler.
- organsnyder 7y agoMockito is a single pom.xml (or build.gradle) entry. edit: Not "Moquitto".
- CraigJPerry 7y agoIt is - but my point is about complexity not ease. The fact that dependency is versioned separately from your code base is one source of complexity that it adds. The fact that it has many lines of code is another complexity. It is however often easier, even although i'm advocating very small self-written test doubles, they are still less easy than using mockito.
- organsnyder 7y agoIt's a dependency that's only needed during testing, though—it's not shipped with production artifacts.
- CraigJPerry 7y agoThat argument doesn't address complexity. Tests are part of the system.
- MockObject 7y agoClose! That's Mockito. https://site.mockito.org/ https://site.mockito.org/
- organsnyder 7y agoOops. Haven't done Java programming full-time since last year, but I did set up a home MQTT broker recently...
- MockObject 7y agoAdding interfaces whose only purpose is unit tests is not keeping the code simpler. Using a test library that automatically generates mocks for unit testing is what keeps the code simpler.
- CraigJPerry 7y ago>> automatically generates mocks for unit testing is what keeps the code simpler You're possibly confusing easy with simple but adding a versioned library of some thousands of lines of code to a project is in no objective way simpler than adding a small test double which you wrote.
- MockObject 7y agoYes, it is, in the only objective way that people actually care about: the size and simplicity of the source code. (What people don't care about is the size of libraries that are scoped for the test context only, and don't even impact artifact size at all.) Also, a Mockito mock is simpler in usage than a hand-written and normally instantiated test double.
- CraigJPerry 7y agoRipping off the classic talk by Rich Hickey: Simple - composed of a single element; not compound Complex - consisting of many different and connected parts Easy - achieved without great effort; presenting few difficulties. This is not as easy but is simple. It is explicit, fundamental Java. public void allocationOnlyPossibleWhenOpen() { LocalDateTime expected = LocalDateTime.now(); TimeAllocator sut = new TimeAllocator(new Clock() { public LocalDateTime getTime() { return expected; } }); sut.setOpen(false); assertThat(sut.allocate()).isNull(); sut.setOpen(true); assertThat(sut.allocate()).isEqualTo(expected); } This is easier but more complex. It depends on a multi-thousand line library with its own API. public void allocationOnlyPossibleWhenOpen() { LocalDateTime expected = LocalDateTime.now(); Clock mockClock = mock(Clock.class); when(clock.getTime()).thenReturn(expected); TimeAllocator sut = new TimeAllocator(mockClock); sut.setOpen(false); assertThat(sut.allocate()).isNull(); sut.setOpen(true); assertThat(sut.allocate()).isEqualTo(expected); } The 2nd example has: A versioned external dependency, externally versioned == opportunity for future conflict. Remember the java logging library wars? Today it's trivial to encounter conflicting spring dependencies on your classpath A complex (but convenient) reflection based implementation, the object has non-obvious behaviour with a clever implementation An entire api surface distinct from the components of your software system Tens of thousands of lines of opportunity for bugs, security risks or just plain old $$ cost to hide in Once you're able to see the world in complexity, it opens up a whole new dimension of cost & quality for your code. All this said, in practice complexity is just a consideration. Often the complexity is worth paying. I don't want to risk the impression that i'm saying never use mockito. If my argument is anything then it's consider the impact of complexity.
- ses1984 7y agoSure pulling in a giant magical reflection heavy test framework has costs but I think it's well worth it.
- chopin 7y agoI do that for this reason but only if I am able to inject the implementation into the classes under test. Very often I just resort to constrictor injection for internal code.
- marcus_holmes 7y agoI did this, and then switched back to just running the code on a test database. I found I was writing two sets of tests: one that tested the code to see if it injected the right parameters into the SQL, and another to see if the SQL worked. It just got easier to test them both together.
- dcolkitt 7y agoThere are tradeoffs to this approach. When the tests break it becomes a lot harder to triangulate the origin of the problems.
- runeks 7y agoI don’t buy this argument. Once you know there’s a bug, you will definitely want to write more tests, in order to find the cause. But always writing a lot of tests, which are only usable in case you find a bug, seems like wasted effort to me. If you need to triangulate the origin, do that when it’s necessary (i.e. when your broad test fails) and not all the time.
- GordonS 7y agoI prefer to just runs tests using a real database too. They might not complete as fast as mocked out ones, but they give me much more confidence of correctness, and still complete in milliseconds.
- breischl 7y agoThis can become a challenge when you want to scale a dev team. You either have people conflicting in the DB, or each dev needs to set up a local DB and deal with DB migrations. Containerized DBs help with this to some extent, but it's still a maintenance headache. There's also a tradeoff around testing error states, but that can be a wash. Some errors are easier to create with a mock, some are easier to create with the actual driver.
- bcrosby95 7y agoNo. This is what many considered test induced damage. If the only reason you create an interface is because a test requires it, you are damaging the integrity of your design. Interfaces are one of the more powerful abstractions available and should be well thought out points of flexibility you specifically designed into your solution. If you use interfaces for other reasons your codebase is full of false flexibility and not-actually-abstracted abstractions which makes it difficult for other people to figure out what they can actually do with it.
- iamaelephant 7y ago> No. This is what many considered test induced damage. If the only reason why you create an interface is because a test requires it, you are damaging the integrity of your design. Writing testable code is not "damage". An interface is a small concession in exchange for the well known benefits of automated testing. I see where you're coming from and many programmers use too many interfaces as a reflex. But I absolutely think interfaces should be used to create seams for automated tests in front of components that do things like interact with databases, file systems, networks etc.
- klmr 7y ago> Writing testable code is not "damage" Correct, but you can go a long way in writing testable code without requiring mock implementations. While I don’t deny the usefulness of mock testing, it comes at a steep cost (= vastly more complex code base), and I increasingly question whether that’s worth it. In practice I find it more useful to unit test what can be tested in isolation, and to perform integration testing for the test, and to limit mock testing (and thus unit tests which would require mocking) to a few well-defined interfaces. This cuts down drastically on boilerplate, with little (if any) loss of testability. And these few remaining mock object dependencies can be modelled via simple parameters and higher-order functions, further cutting down on extraneous interface classes.
- BeetleB 7y ago> Correct, but you can go a long way in writing testable code without requiring mock implementations. For some languages (e.g. C++), if you are using classes, it can actually be fairly challenging to do unit tests without mocks. And almost every solution to this problem out there is to use interfaces/dependency injection/inversion.
- devit 7y agoTesting with mocks is generally a bad idea; instead, one should just test using the same dependencies that are used in production (after testing those dependencies, if also part of the project). This avoids having to introduce the extra interface, having to code the mock, sometimes having to change the mock or tests when changing the code and also lets you catch bugs in the dependencies that the tests on the dependency itself missed and that would otherwise show up in production. One might have to do some work to partially replicate the production setup in testing and making it fast to start and run, but that's usually less work and gives a much better result.
- ludjer 7y agoThat is more integration tests. Unit tests should target logic more then dependencies/integration.
- ludjer 7y agoYou do know you can mock out concrete classes aswell. You do not need a interface.