Reputation: 3339
In my Scala project I have quite a broad & deep hierarchy of classes and packages. Obviously I want to keep the dependencies of packages to a minimum.
I don't know if I can use the compiler for this, so I was wondering how I can use unit tests to validate the independence of packages?
PS: The approach should probably work on bytecode level since I'm using Scala, not Java.
Upvotes: 1
Views: 433
Reputation: 30453
I highly recommend Classycle. It works great on my mixed Scala/Java project. It works at the bytecode level and I have never had a Scala-related issue with it.
On a small-ish project, you can just write your config file by hand. Our project is large-ish, so I wrote a Scala script that generates the config file. The section of the file generated each package looks like:
[agent] = org.nlogo.agent.* excluding org.nlogo.agent.*.*
[agent+] = [agent] [api+]
[agent-] = org.nlogo.* excluding [agent+]
check [agent] independentOf [agent-]
This says that org.nlogo.agent may depend on org.nlogo.api (and recursively on org.nlogo.api's allowed dependencies), but on no other package.
You can also write rules that check external dependencies, so e.g. here's how I check that we only depend on JOGL from certain particular packages:
[JOGL-free-zone] = org.nlogo.* excluding [gl.render] [gl.view]
[JOGL] = net.java.games.* javax.media.opengl.*
check [JOGL-free-zone] independentOf [JOGL]
Upvotes: 1
Reputation: 297205
The scala compiler can generate dependency graphs for you, which you could then check with a home made tool. Here's how you'd call it:
scalac -make:transitive -dependencyfile dep.txt *.scala
And what it generates:
.;C:\Progra~2\Java\jre6\lib\ext\QTJava.zip;C:\Program Files (x86)\Java\jre6\lib\ext\QTJava.zip
-------
Aposta.scala -> Aposta.scala
Aposta.scala -> Premio.scala
Aposta.scala -> Bilhete.scala
Premio.scala -> Premio.scala
Premio.scala -> Sorteio.scala
Bilhete.scala -> Aposta.scala
Bilhete.scala -> Bilhete.scala
Bilhete.scala -> Sorteio.scala
Browser.scala -> Browser.scala
Megasena.scala -> Browser.scala
Megasena.scala -> Bilhete.scala
Megasena.scala -> Sorteio.scala
Megasena.scala -> Megasena.scala
Sorteio.scala -> Premio.scala
Sorteio.scala -> Sorteio.scala
-------
Aposta.scala -> megasena\Aposta$.class
Aposta.scala -> megasena\Aposta$$anonfun$1.class
Aposta.scala -> megasena\Aposta$$anonfun$toString$1.class
Aposta.scala -> megasena\Aposta$$anonfun$toString$2.class
Aposta.scala -> megasena\Aposta.class
Premio.scala -> megasena\Premio$.class
...
Also, sbt does it automatically (look for a file called analysis/dependencies
, as well as analysis/external
in the target dir). This is what they look like:
#Source Dependencies
#Mon Jan 04 18:31:56 BRST 2010
Sorteio.scala=Megasena.scala;Premio.scala;Bilhete.scala
Premio.scala=Sorteio.scala;Aposta.scala
Browser.scala=Megasena.scala
Aposta.scala=Bilhete.scala
Bilhete.scala=Megasena.scala;Aposta.scala
Megasena.scala=
RedBlackTree.scala=
and
#External Dependencies
#Mon Jan 04 18:31:56 BRST 2010
C\:\\Workset\\megasena\\project\\boot\\scala-2.7.4\\lib\\scala-library.jar=Browser.scala;RedBlackTree.scala;Premio.scala
;Bilhete.scala;Sorteio.scala;Megasena.scala;Aposta.scala
C\:\\Arquivos\ de\ programas\\Java\\jdk1.6.0_16\\jre\\lib\\rt.jar=Browser.scala;RedBlackTree.scala;Premio.scala;Bilhete.
scala;Sorteio.scala;Megasena.scala;Aposta.scala
Upvotes: 1
Reputation: 20515
I've played around with JDepend , but not used it in anger. It allows you set up package and class dependency constraints, and validate them with JUnit tests. It's also got a bunch of functionality for finding dependency cycles, calculating a bunch of metrics, and generally keep your architecture clean, shiny, and fresh.
Upvotes: 1
Reputation: 42047
This cannot be done with unit tests, but the compiler is your friend here. Use private[PACKAGENAME] to make a class only visible inside the package. Unlike Java, the class will also be visible in subpackages.
Upvotes: 3