Fakir - mocking important bits of Emacs Lisp
I decided that I would introduce a full stack testing framework into
Elnode, my EmacsLisp event
driven webserver. I started by adding bits of Lisp to the Elnode
module directly, for things like mocking Emacs process objects. That
soon got unwiedly and I realized that some of the things I was doing
might benefit other people. So I made a new package just for these
special mocking functions: fakir.
fakir is for mocking (or faking) Emacs core functions with code
that can be controlled more easily in tests. Right now it only support
files and processes. It's by no means perfect but it is good
enough.
Mocking processes
Processes are complex objects in EmacsLisp, they're really powerful as well. They can be connections to child processes on your operating system or they can be Internet sockets. You can attach arbitary data to them, you can read and write from them, they have buffers and all sorts of other things.
They're important to fake in tests if you want to be able to do anything with them though. So I came up with a way of mocking a process so that the process-functions mostly do the right thing:
(fakir-mock-process ((attrib-1 "an attribute!")) (process-get :fake 'attrib-1)) => "an attribute!"
process buffers can be mocked as well:
(fakir-mock-process ((:buffer "some text in the process buffer!")) (buffer-substring (process-buffer :fake) (point-min) (point-max))) => "some text in the process buffer!"
you can send data to the process:
(fakir-mock-process ((:buffer "some text in the process buffer!\n")) (process-send-string :fake "some more text!") (buffer-substring (process-buffer :fake) (point-min) (point-max))) => "some text in the process buffer! some more text!"
and you can read data from the process:
(fakir-mock-process ((:buffer "some text in the process buffer!")) (process-read-string :fake)) => "some text in the process buffer!"
One problem currently is that all process code in the body of the macro will return the fake values. There is no way to distinguish currently. I haven't really needed to fix this yet but I will at some point.
Mocking files
Files offer a similar problem to processes, you don't want to have to create a bunch of files to have your test code work. Just declare some fake files and the values they should produce. When you're tetsing low level things with Emacs fake files can be invaluable.
Here's a fakir mock file:
(fakir-mock-file (fakir-file
:filename ".bashrc"
:directory "/home/fakeuser")
(expand-file-name "~/.bashrc"))
=> "/home/fakeuser/.bashrc"
a fakir-file is a sort of Common Lisp struct. Separating the
filename and the directory let's us do certain operations simply.
If you want ~ to resolve then the directory has to start with
home - for example:
(fakir-mock-file (fakir-file
:filename "README.md"
:directory "/home/fakeuser/somedir/anotherlevel")
(expand-file-name "~/somedir/anotherlevel/README.md"))
=> "/home/fakeuser/somedir/anotherlevel/README.md"
It's not just the expand-file-name, you can also specify the
mod time for the file:
(fakir-mock-file (fakir-file
:filename "somefile"
:directory "/home/fake"
:mtime "Mon, Feb 27 2012 22:10:21 GMT")
(elt (file-attributes (expand-file-name "~/somefile")) 5))
=> (20299 65357)
Obviously more could be done. We could specify more attributes for files and allow them to be tested.
It would also be really interesting to specify the contents of directories so you could fake those.
Using it all
The best way of using fakir is to declare it as a dependancy in
your package; here's Elnode's prelude:
;;; elnode.el --- a simple emacs async HTTP server ;; Copyright (C) 2010 Nic Ferrier ;; Author: Nic Ferrier <nferrier@ferrier.me.uk> ;; Maintainer: Nic Ferrier <nferrier@ferrier.me.uk> ;; Created: 5th October 2010 ;; Version: 0.9.3 ;; Keywords: lisp, http, hypermedia ;; Package-Requires: ((fakir "0.0.4")(creole "0.8.4"))
If you do that and package your code then you can simply require
the fakir feature and use it in your tests.
See the fakir
project on github.