In scala how can a unit test create a temporary directory to use as part of the testing?
I am trying to unit test a class which depends on a directory
class UsesDirectory(directory : java.io.File) {
...
}
I'm looking for something of the form:
class UsesDirectorySpec extends FlatSpec {
val tempDir = ??? //missing piece
val usesDirectory = UsesDirectory(tempDir)
"UsesDirectory" should {
...
}
}
Also, any comments/suggestions on appropriately cleaning up the resource after the unit testing is completed would be helpful.
Thank you in advance for your consideration and response.
Krzysztof's answer provides a good strategy for avoiding the need for temp directories in your tests altogether.
However if you do need UsesDirectory to work with real files, you can do something like the following to create a temporary directory:
import java.nio.file.Files
val tempDir = Files.createTempDirectory("some-prefix").toFile
Regarding cleanup, you could use the JVM shutdown hook mechanism to delete your temp files.
(java.io.File does provide deleteOnExit() method but it doesn't work on non-empty directories)
You could implement a custom shutdown hook using sys.addShutdownHook {}, and use Files.walk or Files.walkTree to delete the contents of your temp directory.
Also you may want to take a look at the better-files library, which provides a less verbose scala API for common files operations including File.newTemporaryDirectory() and file.walk()
File in Java is very cumbersome to test. There is no simple way to create some kind of virtual filesystem abstraction, which can be used for tests.
A cool way around it is to create some kind of wrapper, that can be used for stubbing and mocking.
For example:
trait FileOps { //trait which works as proxy for file
def getName(): String
def exists(): Boolean
}
object FileOps {
class FileOpsImpl(file: File) extends FileOps {
override def getName(): String = file.getName //delegate all methods you need
override def exists(): Boolean = file.exists()
}
implicit class FromFile(file: File) { //implicit method to convert File to FileOps
def toFileOps: FileOpsImpl = new FileOpsImpl(file)
}
}
Then you'd have to use it instead of File in your class:
class UsesDirectory(directory : FileOps) {
...
}
//maybe you can even create implicit conversion, but it's better to do it explicitly
val directory = new UserDirectory(file.toFileOps)
And what is benefit of that?
In your tests you can provide custom implementation of FileOps:
class UsesDirectorySpec extends FlatSpec {
val dummyFileOps = new FileOps {
override def getName(): String = "mock"
override def exists(): Boolean = true
}
//OR
val mockFileOps = mock[FileOps] //you can mock it easily since it's only trait
val usesDirectory = UsesDirectory(dummyFileOps)
"UsesDirectory" should {
...
}
}
If you use this or a similar approach, you don't even need to touch filesystem in your unit test.
Related
I need to mock (with scalamock 4.4.0/scalatest 3.2.9) the behaviour of Hadoop file system. I found out, that I can't mock it directly, so I'm trying to get around this problem.
On the first, try I created wrapper (in the scope of main logic):
class HadoopFsWrapper(path: Path)(implicit fs: org.apache.hadoop.fs.FileSystem) {
def listStatus(path) = fs.listStatus(path)
}
class HadoopService(path: Path)(implicit val fs: HadoopFsWrapper){
def someLogic() = fs.listStatus(path)
}
I declare this class both in main and test code with defining of implicit HadoopFsWrapper.
implicit val fs = mock[HadoopFsWrapper]
val hs = HadoopService(path)
// other test logic
And this done well.
But I don't think, its a good idea to modify main code for test purpose.
Other way I considered is to create wrapper only in test scope and apply some kind of "decorator" to deceive HadoopService, which expects FileSystem to be defined in main scope:
// only in test scope
class HadoopFsWrapper(path: Path) extends org.apache.hadoop.fs.FileSystem {
def listStatus(path) = listStatus(path)
// dummies for implemented FileSystem-methods
}
object HadoopFsWrapper {
def apply(): org.apache.hadoop.fs.FileSystem = new HadoopFsWrapper
}
// main logic
class HadoopService(path: Path)(implicit val fs: org.apache.hadoop.fs.FileSystem){
def someLogic() = fs.listStatus(path)
}
But when I'm trying to mock HadoopFsWrapper it's defined as null.
I have to create a file loader object and I would like the file to be loaded only once at object creation.
What I did until now is create a trait with a method read that will read file and output a list of String.
trait Loader {
protected val readSource: List[String] = {
Source
.fromInputStream(getClass.getResourceAsStream("filename"), "UTF-8")
.getLines()
.toList
}
def transform(delimeter: String): Vector[C] = {
val lines = readSource
// process the lines
}
}
The trait is implemented by several object, and the transform method can be called multiple times in the client code.
I would like to avoid re reading the file each time the transform method is called and my first solution was to extract the val lines = readSource from the transform method and make a function of it def loadFile = readSource and to create a apply method in my objects to call loadFile like so :
object MyLoader extends Loader {
def apply: List[String] = {
loadFile
}
}
I am wondering if this is the right way to do it. Thank you for your advices.
If you want the resource read once for all, then you should do that in a singleton object, which will be initialized once lazily.
Clients should use that object. "Prefer composition over inheritance" is the mantra.
If you want a mix-in that makes it easy to use the object, you can use "self-types" to constrain clients:
trait HasResource { val resource: R = TheResource }
trait Client { self: HasResource => def getR: R = resource }
This is the "cake pattern" way of making stuff available.
I am trying to test
trait Name extends Helper {
def name() = {
var s = getSystem()
s.name()
}
}
where all I want to do is make sure that the "s.name()" method is invoked once by mocking an instance of s, which is a System.
Helper is defined as so:
trait Helper {
def getSystem() : System = {
systemGetter.get()
}
}
As of now, my NameSpec looks something like:
class NameSpec extends FlatSpec with Matchers with MockitoSugar {
class NameImpl extends Name
var toTest = new NameImpl
val mockSystem = mock[System]
it should "call s.name() once" in {
when(getSystem() is invoked, return a mockSystem)
toTest.name()
// Verify that mockSystem.name() happened only once
}
}
What I'm confused about is how to return a mock System in toTest.name() when it calls getSystem() so that I can verify that the system calls s.name() only once. I could easily mock this System if it were a parameter to the name() method in Name trait, so I guess I don't know how to "inject" a mockSystem to be used instead of a real system when that method is invoked.
Unfortunately your code is not compilable and thus is obviously an inadequate representation of what you really have. Particularly it is not clear how the Helper really gets an object of type System. I think that in the real code you should mock the systemGetter, that I suppose is somehow injected into the objects implementing Helper trait, to return your mockSystem. However it is hard to show you a valid example of that basing on the code you provided. If for some reason this is not what you can do, there are a few more avenues.
You seem to use something like Cake pattern around Helper and its inheritance. If so, you can use a class instead of NameImpl to inject System:
class NameWithInjectedSystem(val system: System) extends Name {
override def getSystem(): System = system
}
it should "call s.name() once" in {
val mockSystem = mock[System]
val nameToTest = new NameWithInjectedSystem(mockSystem)
val mockName = "Mock name"
when(mockSystem.name()).thenReturn(mockName)
val actual = nameToTest.name()
actual should === (mockName)
verify(mockSystem, times(1)).name()
}
Finally you can mock even nameToTest object itself but this is not the way I'd suggest because it binds test to much more implementation details than you should want:
it should "call s.name() once" in {
val mockSystem = mock[System]
val nameToTest = mock[NameImpl]
when(nameToTest.getSystem()).thenReturn(mockSystem)
when(nameToTest.name()).thenCallRealMethod()
val mockName = "Mock name"
when(mockSystem.name()).thenReturn(mockName)
val actual = nameToTest.name()
actual should ===(mockName)
verify(mockSystem, times(1)).name()
}
Note how you must call thenCallRealMethod for the .name() call and so you should do for all the calls inside the name or the test will not work.
I need to write an integration test and it requires starting a server executable. I want to make location of the server configurable, so that I could set it on my box and on integration server.
ConfigMapWrapperSuite seems to be doing exactly what I want:
#WrapWith(classOf[ConfigMapWrapperSuite])
class ConsulTest(configMap: ConfigMap) extends FlatSpec with ShouldMatchers {
val consulPath = configMap("consul.path")
"Consul" should "list keys under root" in {
...
}
But when I set my IDE (IntelliJ) to execute all tests in the project, I get an exception saying that constructor with Map parameter not found. Looking into source code of scalatest revealed:
final class ConfigMapWrapperSuite(clazz: Class[_ <: Suite]) extends Suite {
private lazy val wrappedSuite = {
val constructor = clazz.getConstructor(classOf[Map[_, _]])
constructor.newInstance(Map.empty)
}
So in contrary to what documentation says, suite must have constructor with a Map and not ConfigMap.
Ok, I changed constructor to take a Map[String,String] but now I get NoSuchElementException at val consulPath = configMap("consul.path"). Lookung up the stack down to ConfigMapWrapperSuite and I see that constructor.newInstance(Map.empty) WTF? So wrapped suite class is instantiated with empty map, and than another time, during the suite run with actual map of parameters? How do I suppose to get parameters if I'm given an empty map?
I looked up scalatest's unit tests. They are so rudimentary that actually retrieving a value from configMap is not performed.
I do not want to use ConfigMapFixture because it will make me initializing every single test with the same code.
So, how do I not only pass but also get global setting in test suite?
Scalatest version: 3.0.0-M15
Ok, answering my own question. ConfigMapWrapperSuite seems to be not used too much and essentially is broken.
Instead I've used BeforeAndAfterAllConfigMap as in here:
class ConsulTest extends FlatSpec with ShouldMatchers with OneInstancePerTest with BeforeAndAfterAllConfigMap {
var consulProcess: Process = null
override def beforeAll(conf: ConfigMap): Unit = {
consulProcess = Seq("bin/"+exe, "agent", "-advertise", "127.0.0.1", "-config-file", "bin/config.json").run()
}
override def afterAll(conf: ConfigMap): Unit = {
consulProcess.destroy()
}
You need override instance
#WrapWith(classOf[ConfigMapWrapperSuite])
class AcceptanceTest(configMap: Map[String, Any])
extends FlatSpec
with OneInstancePerTest {
override def newInstance = new AcceptanceTest(configMap)
}
In Scala, is there there anything wrong with using the below method of dependency injection.
// Define an interface
trait FileStorage {
def readFile(filename:String):OutputStream
}
// And an implementation
class S3FileStorage extends FileStorage {
def readFile(filename:String):OutputStream = ???
}
// Define our service as a trait with abstract fields that need to be
// injected in order to construct. All implementation details go here.
trait FileHTTPServer {
val fileStorage:FileStorage
def fetchFile( session:Session, filename:String ) = ???
}
Now we wire things up
// Wire up a concrete file service that we actually use in code
// No implementation details should go here, we're simply wiring up a FileHttpServerl
// An entire project could be wired up this way in a central location if desired.
object S3FileHttpServer extends FileHTTPServer {
val fileStorage = new S3FileStorage
}
// We could also do this anonymously
val myHttpServer = new FileHttpServer {
val fileStorage = new S3FileStorage
}
// Or create a mocked version for testing
val mockedHttpServer = new FileHttpServer {
val fileStorage = mock[FileStorage]
}
Obviously the Cake pattern provides more flexibility (particularly around self-types), however for simpler use cases this has much less boilerplate, while still providing compile time checking and a clean unambiguous interface.
Yes, this is absolutely fine approach. And yes, sometimes you can use constructor injection, nothing wrong with that too. But with constructor injection you have to propagate your dependencies manually, while with cake pattern your dependencies are propagated automatically via self-type annotations. So for big projects constructor injection actually lead to more boilerplate than cake pattern, especially at the construction site (where you create all your objects and set up dependencies between them).
However, what you have presented is not full-fledged cake pattern. In real cake pattern there is an additional layer around business logic classes, so-called components, and you do not wire up logic classes directly but components instead.
trait FileStorageComponent {
def fileStorage: FileStorage
trait FileStorage {
def readFile(filename: String): OutputStream
}
}
trait S3FileStorageComponent extends FileStorageComponent {
val fileStorage = new S3FileStorage
class S3FileStorage extends FileStorage {
def readFile(filename: String): OutputStream = ???
}
}
trait FileHttpServerComponent {
self: FileStorageComponent =>
val fileHttpServer = new FileHttpServer
class FileHttpServer {
def fetchFile(session: Session, filename: String) = ???
}
}
// Wiring
object S3FileHttpServer extends FileHttpServerComponent with S3FileStorageComponent
// Anonymous
val server = new FileHttpServerComponent with S3FileStorageComponent
// Mocking
object TestFileHttpServer extends FileHttpServerComponent with FileStorageComponent {
val fileStorage = mock[FileStorage]
}
In this approach there are more boilerplate in traits definitions, but in return you have greater flexibility and very clear dependency management on the use place. For example, here is how program entry point in one of my projects looks like:
object Main
extends MainUI
with DefaultActorsManagerComponent
with DefaultPreferencesAccessComponent
with DefaultModelComponent
with DefaultMainWindowViewComponent
with DefaultMainWindowControllerComponent
with MainWindowReporterComponent
with DefaultClientActorComponent
with DefaultResponseParserActorComponent
with DefaultArchiverActorComponent
with DefaultMainWindowAccessActorComponent
with DefaultUrlParserComponent
with DefaultListenerActorComponent
with DefaultXmlPrettifierComponent
All main program components are in one place. Pretty neat IMO.