2

I'll often make changes to my init.el and find that after I M-x load-file, the results can be different next time I open emacs. Or maybe while one thing I modified gets better, something else gets clobbered and I want to revert. However, I may have assumed everything was fine and piled many init.el changes on top since then.

So, I'd like to run tests on the effects of changes before I finalize them.

Can I run a test suite in my .emacs.d/init.el or is that insane? Is there even a way to do that, like to test that opening files in python-mode or rust-mode works consistently after changes? How do emacsers usually handle the development process of testing packages while keeping the good things nailed down?

1

4 Answers 4

3

Any time you edit your init file, make a habit of immediately executing the code you changed with C-x C-e (eval-last-sexp). If it gives you an error then you know the code is incorrect right away. If the settings you have chosen aren’t exactly right, you’ll be able to see their effect and make corrections.

3

One more possibility: add a tester (e.g.) user to your system, copy the modified init file to that user's /home/tester/.emacs (or .emacs.d/init.el or .config/emacs/init.el) and then try to start emacs with that user's init file: emacs --user tester. That way, you can still fall back to your own init file and only update it if the tester user does not complain :-)

2

Here's an approach from u/clemera (in turn inspired by abo-abo) on reddit which can be used to ensure you never exit your current Emacs session when your config is broken (you will need to install async.el also):

(defun test-emacs ()
  "Test if emacs starts correctly."
  (interactive)
  (if (eq last-command this-command)
      (save-buffers-kill-terminal)
    (require 'async)
    (async-start
     (lambda () (shell-command-to-string
                 "emacs --batch --eval \"
(condition-case e
    (progn
      (load \\\"~/.emacs.d/init.el\\\")
      (message \\\"-OK-\\\"))
  (error
   (message \\\"ERROR!\\\")
   (signal (car e) (cdr e))))\""))
     `(lambda (output)
        (if (string-match "-OK-" output)
            (when ,(called-interactively-p 'any)
              (message "All is well"))
          (switch-to-buffer-other-window "*startup error*")
          (delete-region (point-min) (point-max))
          (insert output)
          (search-backward "ERROR!"))))))

Then you can for example bind test-emacs to C-x C-c, invoking once will tell you if everything is alright, and a second in succession will exit.

In terms of reverting unwanted changes to your config, many users commit their init files to source control which can make this a lot easier (if you aren't already).

http://wikemacs.org/wiki/User's_Initialization_File#Debugging_the_Init_file may also be helpful.

3
  • This is interesting. Is there a way or hook I could use to always automatically call test-emacs on save, without having to manually call C-x C-c ? Commented Jan 2, 2023 at 22:41
  • This is after-save-hook ? oremacs.com/2015/03/05/testing-init-sanity Commented Jan 2, 2023 at 22:44
  • 1
    Yeah I think after-save-hook would do what you want (though you'd want to test buffer-file-name = init.el in your hook function) Commented Jan 3, 2023 at 10:36
1

You have arrived at a place that all diligent programmers reach sooner or later. The general case solution is known as "regression testing" which aims to automatically detect the kind of regressions you describe.

As usual in Emacs, there are multiple specific solutions. The most widely-used one is ERT, for "Emacs Lisp Regression Testing". It is built in, so there's no need to install it separately.

https://www.gnu.org/software/emacs/manual/html_node/ert/

You define tests which specify inputs and make "assertions" regarding corresponding expected outputs and side effects. You then run either an individual test, a set of tests grouped using tags, or all the tests in your init file, and receive feedback indicating which tests passed and which if any failed.

(defun ph/mean (&rest sample)
  "Total SAMPLE and divide by its size."
  (/ (cl-reduce #'+ sample) (+ (length sample) 0.0)))

(ert-deftest ph/test-mean ()
  "Test function `ph/mean'."
  :tags '(maths statistics numbers)
  (should
   (= 2 (ph/mean 1 2 3))))

Run M-x ert RET RET to run all your tests and see a chart of color-coded results. You should do this regularly, perhaps daily, as well as every time you create or change any Elisp code, upgrade or install external packages, and so on.

As a beneficial side effect, using regression tests encourages you to write better code: small, independent, side-effect-free functions that are easy to test.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.