Skip to content

Guidelines

Cleary, Paul edited this page Nov 9, 2015 · 1 revision

Testing

  • Test coverage must remain above 90% for both line and branch coverage.
  • Use ScalaTest for all tests
  • Use the AkkaTestJawn for all tests in money-core that tests actors
  • Use ScalaTest WordSpec for unit tests and Akka tests
  • Use ScalaTest FeatureSpec for integration tests
  • Use ScalaTest GivenWhenThen to add clarity to all tests when clarity is needed
  • Use the Akka Testkit for doing all Actor testing
  • Use Mockito when needed. We tend to use more integrated functional test (Akka Testkit, Integration Tests) as opposed to using Mocks. If you have to mock, make sure that you don't have a suitable way to do the same test using the Testkit or Integration tests

Dependency Injection...NOT!

We do not use any dependency injection library in Money. This is done specifically to limit the dependencies that are sucked into Money.

But, we do need to have a way to unit test, so we will need at times need to create test stand ins or use mock objects.

  • Use CONSTRUCTOR arguments to pass in depedencies. Yes, this is the ultimate dependency injection mechanism!
  • Use the ActorMaker trait for Actors in order to create child actors, Use the AkkaTestJawn to test this. We have a nice little harness that we use in order to capture child actor creation and substitue with test probes instead. Use it, or suffer the consequences (consequences TBD).
  • Limit your use of advanced injection techniques like the Cake pattern. Yes, the cake pattern and self type annotations are super cool, but they should be consistently applied across the codebase. If you cannot use a constructor argument or ActorMaker, you should have a darn good reason.

Naming

Constants are UpperCamelCase

The motivation here is purely readability. While the SNAKE_CASE makes something stand out, using them regularly makes the code difficult to read.

  • DO name your constant like val FormatString = "%s.format.%s"
  • DON'T name your constant like snake case `val FORMAT_STRING = "%s.format.%s"

Methods are named lowerCamelCase

  • DO def doSomething(foo:String)
  • DON'T def this_is_scala_not_python(jerk:You)

Do not prefix methods with get for properties or methods with no side effects

get is a Javabean standard, and clutters the code. It is much easier to read person.firstName as opposed to getPerson.getFirstName


Omit parens for all methods that have no side effects

parens add unnecessary clutter to the code, especially when retrieving a value. Again, person.firstName is much more readable than person().firstName()

This even applies to java getters, so javabean.getField as opposed to javabean.getField(), and obj.toString as opposed to obj.toString()


Use "action" names for methods that do have side effects

When saving data to a persistent store:

  • DO call the method saveData(something:Person)
  • DON'T call the method data(something:Person)

Call functions that have side effects with parens, even if they take no parameters

  • DO call the function machine.reboot()
  • DON'T call the function machine.reboot

Use Joda's DateTimeUtils.currentTimeMillis() instead of System.currentTimeMillis(). This supports testing.

Handling Nulls

We don't handle nulls (or at least we do everything in our power to avoid them). If it is possible that a variable or member to not have a value, then use the scala.Option. You see Option everywhere in the code, get used to it, it is awesome.

  • DO class Person(val middleName:Option[String] = None)
  • DON'T class Person(val middleName:String = null) (if you do this, then you will be made an example of in front of the world

Using Case Classes

case classes are a super duper scala construct that will automatically generate properties for a class, generate equals and hashcode, and do other awesome stuff. The most awesome thing they can easily do is being used in "pattern matching". They also are used by other libraries like JSON and XML. So get used to them, and feel good about it.

  • DO case class Person(firstName:String,lastName:String)
  • DON'T class Person(val firstName:String, val lastName: String)