Skip to content

Commit

Permalink
✅ Improve FileWriterTest
Browse files Browse the repository at this point in the history
Signed-off-by: Leonardo Colman Lopes <[email protected]>
  • Loading branch information
LeoColman committed Feb 4, 2025
1 parent e14454b commit 39dff2e
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -1,73 +1,32 @@
package br.com.colman.petals.use.io.output

import androidx.test.platform.app.InstrumentationRegistry
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import br.com.colman.kotest.FunSpec
import io.kotest.core.spec.IsolationMode.InstancePerTest
import io.kotest.matchers.file.shouldExist
import io.kotest.matchers.file.shouldNotExist
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldEndWith
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import java.io.File
import java.time.LocalDate

class FileWriterTest : FunSpec({

val context = InstrumentationRegistry.getInstrumentation().targetContext
val exportsDirectory = File(context.filesDir, "exports")
val context: Context = ApplicationProvider.getApplicationContext()
val target = FileWriter(context)
val exportDir = File(context.filesDir, "exports")

beforeEach { exportsDirectory.deleteRecursively() }

test("Creates exports directory when it doesn't exist") {
exportsDirectory.shouldNotExist()

target.write(FakeContent1)

exportsDirectory.shouldExist()
test("should create export directory when using context constructor") {
exportDir.deleteRecursively()
FileWriter(context)
exportDir.shouldExist()
}

test("Replaces an existing file") {
val uri1 = target.write(FakeContent1)
val uri2 = target.write(FakeContent2)
test("should write content to file and generate correct file name") {
val content = "Integration test data"
val expectedFileName = "PetalsExport-${LocalDate.now()}.csv"
val expectedFile = File(exportDir, expectedFileName)

uri1 shouldBe uri2
target.write(content)

val content = context.contentResolver.openInputStream(uri2)!!
content.bufferedReader().readText() shouldBe FakeContent2
expectedFile.shouldExist()
expectedFile.readText() shouldBe content
}

test("Uses PetalsExport-date.csv as the default file name") {
val uri = target.write(FakeContent1)

uri.path shouldEndWith "PetalsExport-${LocalDate.now()}.csv"
}

test("Generates different file names on different dates") {
val fixedDate = LocalDate.of(2024, 2, 9)
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns fixedDate

val uri1 = target.write(FakeContent1)

val newDate = fixedDate.plusDays(1)
every { LocalDate.now() } returns newDate

val uri2 = target.write(FakeContent2)

uri1 shouldNotBe uri2

uri1.path shouldEndWith "PetalsExport-$fixedDate.csv"
uri2.path shouldEndWith "PetalsExport-$newDate.csv"

unmockkStatic(LocalDate::class)
}

isolationMode = InstancePerTest
})

private const val FakeContent1 = "abc"
private const val FakeContent2 = "def"
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,32 @@ import br.com.colman.petals.BuildConfig.APPLICATION_ID
import java.io.File
import java.time.LocalDate

class FileWriter(private val context: Context) {
class FileWriter(
private val exportDirectory: File,
private val fileToUriConverter: (File) -> Uri,
) {

private val exportsDir by lazy { getExportDirectory() }
init {
if (!exportDirectory.exists()) exportDirectory.mkdirs()
}

constructor(context: Context) : this(
File(context.filesDir, "exports"),
{ getUriForFile(context, APPLICATION_ID, it) }
)

fun write(content: String): Uri {
val exportedFile = createAndWriteFile(content)
return getUriForFile(context, APPLICATION_ID, exportedFile)
return fileToUriConverter(exportedFile)
}

private fun createAndWriteFile(content: String): File {
val fileName = generateFileName()
return File(exportsDir, fileName).apply { writeText(content) }
return File(exportDirectory, fileName).apply { writeText(content) }
}

private fun generateFileName(): String {
val date = LocalDate.now()
return "PetalsExport-$date.csv"
}

private fun getExportDirectory() = File(context.filesDir, "exports").apply {
if (!exists()) mkdirs()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import org.koin.dsl.module
val UseOutputModule = module {
single { UseCsvHeaders(get()) }
singleOf(::UseCsvSerializer)
singleOf(::FileWriter)
single { FileWriter(get()) }
singleOf(::UseExporter)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package br.com.colman.petals.use.io.output

import android.net.Uri
import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir
import io.kotest.matchers.file.shouldExist
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import java.io.File
import java.time.LocalDate

class FileWriterTest : FunSpec({

val exportDir = tempdir()
val fileToUri = mockk<(File) -> Uri>()

val target = FileWriter(exportDir, fileToUri)

test("should create export directory if it does not exist") {
exportDir.deleteRecursively()

FileWriter(exportDir, fileToUri)

exportDir.shouldExist()
}

test("should generate correct file name and write to file") {
val expectedFileName = "PetalsExport-${LocalDate.now()}.csv"
val expectedFile = File(exportDir, expectedFileName)
val expectedUri = mockk<Uri>()
every { fileToUri(expectedFile) } returns expectedUri

target.write("Test Content")

expectedFile.shouldExist()
expectedFile.name shouldBe expectedFileName
}

test("should write content to file and return its URI") {
val content = "Sample data"
val expectedFile = File(exportDir, "PetalsExport-${LocalDate.now()}.csv")
val expectedUri = mockk<Uri>()

every { fileToUri(expectedFile) } returns expectedUri

val resultUri = target.write(content)

expectedFile.shouldExist()
expectedFile.readText() shouldBe content
resultUri shouldBe expectedUri

verify { fileToUri(expectedFile) }
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import android.content.Context
import android.content.res.Resources
import br.com.colman.petals.use.repository.UseRepository
import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldNotBe
import io.mockk.every
import io.mockk.mockk
import org.koin.dsl.koinApplication
import org.koin.dsl.module
Expand All @@ -16,7 +18,9 @@ class UseOutputModuleTest : FunSpec({
module {
single { mockk<UseRepository>() }
single { mockk<Resources>(relaxed = true) }
single { mockk<Context>() }
single { mockk<Context> {
every { filesDir } returns tempdir()
} }
}
)
}.koin
Expand Down

0 comments on commit 39dff2e

Please sign in to comment.