View Source Testing
This guide covers testing strategies for applications using Buckets, including unit tests, integration tests, and testing with live cloud services.
Test Setup
Basic Test Configuration
# config/test.exs
config :my_app, MyApp.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "tmp/test_storage",
base_url: "http://localhost:4001"
# Ensure test isolation
config :my_app, :storage_base_path, "tmp/test_#{System.get_pid()}"
Test Helper Module
defmodule MyApp.StorageCase do
use ExUnit.CaseTemplate
using do
quote do
import MyApp.StorageCase
setup :create_test_storage
end
end
def create_test_storage(_context) do
# Create unique test directory
test_dir = "tmp/test_#{:rand.uniform(1_000_000)}"
File.mkdir_p!(test_dir)
# Configure for this test
config = [
adapter: Buckets.Adapters.Volume,
bucket: test_dir,
base_url: "http://localhost:4001"
]
MyApp.Cloud.put_dynamic_config(config)
on_exit(fn ->
# Cleanup
File.rm_rf!(test_dir)
end)
{:ok, storage_dir: test_dir}
end
def create_test_object(attrs \\ %{}) do
defaults = %{
uuid: Ecto.UUID.generate(),
filename: "test.pdf",
content: "test content",
content_type: "application/pdf"
}
attrs = Map.merge(defaults, attrs)
Buckets.Object.new(
attrs.uuid,
attrs.filename,
metadata: %{
content_type: attrs.content_type,
content_size: byte_size(attrs.content)
}
)
end
end
Unit Tests
Testing Storage Operations
defmodule MyApp.CloudTest do
use MyApp.StorageCase
describe "insert/1" do
test "stores object successfully", %{storage_dir: dir} do
object = create_test_object()
assert {:ok, stored} = MyApp.Cloud.insert(object)
assert stored.stored?
assert stored.location.path =~ object.uuid
# Verify file exists
file_path = Path.join([dir, stored.location.path])
assert File.exists?(file_path)
end
test "handles missing file" do
object = Buckets.Object.from_file("non_existent.pdf")
assert_raise RuntimeError, ~r/non-existent file/, fn ->
MyApp.Cloud.insert!(object)
end
end
end
describe "read/1" do
test "reads stored object data" do
object = create_test_object(content: "Hello, World!")
{:ok, stored} = MyApp.Cloud.insert(object)
assert {:ok, data} = MyApp.Cloud.read(stored)
assert data == "Hello, World!"
end
test "returns error for missing object" do
fake_object = create_test_object()
|> Map.put(:location, Buckets.Location.new("fake/path", MyApp.Cloud))
|> Map.put(:stored?, true)
assert {:error, :not_found} = MyApp.Cloud.read(fake_object)
end
end
end
LiveView Testing
Testing Direct Uploads
defmodule MyAppWeb.UploadLiveTest do
use MyAppWeb.ConnCase
use MyApp.StorageCase
import Phoenix.LiveViewTest
describe "file uploads" do
test "direct upload to cloud storage", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/uploads/new")
# Simulate file selection
file =
file_input(view, "form", :document, [
%{
name: "invoice.pdf",
content: File.read!("test/fixtures/sample.pdf"),
type: "application/pdf"
}
])
# Trigger upload
assert render_upload(file, "invoice.pdf")
# Submit form
html = form(view, "form") |> render_submit()
assert html =~ "Upload successful"
assert MyApp.Files.count() == 1
end
end
end
Mocking and Stubbing
Using Mox for Cloud Operations
# test/support/mocks.ex
Mox.defmock(MyApp.CloudMock, for: Buckets.Cloud)
# In tests
defmodule MyApp.ServiceTest do
use ExUnit.Case
import Mox
setup :verify_on_exit!
test "processes file after upload" do
object = create_test_object()
expect(MyApp.CloudMock, :insert, fn ^object ->
{:ok, %{object | stored?: true}}
end)
expect(MyApp.CloudMock, :read, fn _object ->
{:ok, "processed content"}
end)
assert {:ok, result} = MyApp.FileProcessor.process(object)
assert result.content == "processed content"
end
end
Running Tests
# Run all tests
mix test
# Run only unit tests (exclude external)
mix test --exclude external
# Run specific adapter tests
mix test --only s3
# Run with coverage
mix test --cover