Scala testable code with inheritance and mixins

I developed a lot of Java code and tried in Groovy and Haskell, which now led me to Scala.

I feel relatively comfortable with the functional side of Scala, but I find myself a little shaky in the object-oriented design in Scala because it is slightly different from Java, in particular because of the -ins features / mix.

I'm trying to write code that can be checked as much as possible, which in my Java development has always focused on

  • Equanimity, where possible.
  • Prefer state injection by designers
  • ALWAYS go to composition instead of inheritance (heavily influenced and possibly overreacted to this post on SO )

Now I'm trying to land on my feet in this new Scala territory, and it's hard for me to decide which approach should I go here, in particular, should I start using inheritance for some purposes.

Scala programming (Wampler and Payne; O'Reilly, 2nd Edition) contains a section with considerations (โ€œGood Object Oriented Design: A Retreatโ€), and I read a few posts about SO, but I did not see any explicit mention of test control. The book offers this advice on using inheritance:

  • An abstract base class or attribute is subclassed one level by specific classes, including case classes.
  • Concrete classes are never subclasses, except in two cases:
    • Classes that mix in other types of behavior defined in traits (...)
    • Tested versions for promoting an automated billboard.
  • When the subclasses seem correct, consider the behavior of the tiling and mix them in these traits.
  • Never share a logical state between boundaries of type parent-child.

Some SO digging also suggests that sometimes mixing is preferable to composition .

Therefore, I have two questions:

  • Are there common cases where it would be better, even considering the possibility of validation, to use inheritance?

  • Does mixing have good ways to increase the testability of my code?

+6
source share
2 answers

The use of the trait in Q / A that you referenced really dealt with the flexibility provided by blending traits.

In the example, when you explicitly decrypt a trait, the compiler blocks the types of the class and superclass at compile time. In this example, MyService is LockingFlavorA

 trait Locking { // ... } class LockingFlavorA extends Locking { //... } class MyService extends LockingFlavorA { } 

When you used typed self-determination (as shown in Q / A that you pointed to):

 class MyService { this: Locking => } 

.. Locking can refer to Locking itself or to any valid Locking subclass. Then the author mixes up in the implementation of blocking on the call site without creating a new class for this purpose:

 val myService: MyService = new MyService with JDK15Locking 

I think when they say that you can make testing easier, they really talk about using this function to mimic what we Java developers usually do with compositions and mock objects. You simply execute the Locking layout and mix it during the test and do the real implementation for the runtime.

To your question: is this better or worse than using a mock library and dependency injection? It would be difficult to say, but I think that in the end many of them will go on how well one method or another plays with the rest of your code base.

If you are already successfully applying composition and addiction, I would consider continuation of this model to be a good idea.

If you are just starting out and really do not need all this artillery, or if you have not philosophically decided that dependency injection is right for you, you can get many miles from mixins for a very low cost in execution complexity.

I think that the true answer will be very situational.

TL DR lower

Question 1) I think this is a situationally useful alternative to / dep -inj, but I do not think that it provides any significant gain, except perhaps for simplicity.

Question 2) Yes, this can improve testability, mainly by emulating simulated objects through the implementation of features.

+8
source

I did, could experience using a combination of mixing and composition.

therefore, as an example, use a component to mix behavior into a specific feature. The example below shows a structure using several dao attributes in a class.

 trait ServiceXXX { def findAllByXXX(): Future[SomeClass] } trait ServiceYYY { def findAllByYYY(): Future[AnotherClass] } trait SomeTraitsComponent { val serviceXXX: ServiceXXX val serviceYYY: ServiceYYY } trait SomeTraitsUsingMixing { self: SomeTraitsComponent => def getXXX() = Action.async { serviceXXX.findAllByXXX() map { results => Ok(Json.toJson(results)) } } def getYYY() = Actiona.async { serviceYYY.findAllByYYY() map {results => Ok(Json.toJson(results)) } } } 

After that, you can declare a specific component and bind it to an example to a companion object:

 trait ConreteTraitsComponent extends SomeTraitsComponent { val serviceXXX = new ConcreteServiceXXX val serviceYYY = new ConcreteServiceYYY } object SomeTraitsUsingMixing extends ConreteTraitsComponent 

Using this template, you can easily create a test component and use mock to test the specific behavior of your tait / class:

 trait SomeTraitsComponentMock { val serviceXXX = mock[ServiceXXX] val serviceYYY = mock[ServiceYYY] } object SomeTraitsUsingMixingMock extends SomeTraitsComponentMock 

And in the specification, you can declare control of service results using ScalaMock http://scalamock.org/

+3
source

Source: https://habr.com/ru/post/982492/


All Articles