Automated Unit and integration test frameworks

Hello,
I am looking for automated unit and integration testing frameworks and methods for Ocaml. My IDE is emacs + tuareg + merlin. I can try VS Code too.
But I was coding examples. Now I want to build a project, pull from git and run tests. What is the recommended way to do this ?

I also read about ‘dune’ in this forum. Is that the proper build and dependency management system ?

Thanks,
Mohan

1 Like

So now I have set up dune to build the code. I thought Dune will pull dependencies from an external public repository like Maven.does That was what I asked earlier. Appears that I have to install OUnit using opam first.
I thought that the dependency management system will somehow setup the editor to load the dependencies and I can just use open OUnit2
Eclipse and IntelliJ have spoilt me.

1 Like

dune does the builds (so takes care of build dependencies) but opam does the packaging. opam in conjunction with tuareg does indeed ensure that the editor knows about new deps, but it’s true you need to go through opam to install them. I usually have a terminal open with dune build -w running, so it will rebuild on any changes, and ensure dependencies are kept up to date (e.g., if I add a new module or component somewhere in the project).

For testing, I am fond of qcheck for property based testing and alcotest for the test runner and unit tests. But OUnit is widely used, so I’m sure this is a good choice :slight_smile:

re: test automation, I have used circleCI for automating CI without much fuss. You can see my approach here. You may also be interested in the new GitHub actions announced in GitHub Actions for OCaml / opam now available

I think better opam/dune integration (or a wrapper tool to manage their interaction) will be coming before long, and it will make it much easier to manage dependencies. In the meantime, I have taken to including a Makefile with a rule like this:

.PHONY: deps
deps: $(opam_file)

# Update the package dependencies when new deps are added to dune-project
$(opam_file): dune-project
	-dune build @install         # Update the $(opam_file)
	-git add $(opam_file)        # opam uses the state of master for it updates
	-git commit $(opam_file) -m "Updating package dependencies"
	opam install . --deps-only   # Install the new dependencies

Then make deps will update the opam package file and install new deps as needed. This depends on dune generation of opam files.

2 Likes

I set up Dune to build and execute my code. But I still have some questions. Hope I am not testing your patience :slight_smile:

  • I have a .opam file and a dune file showing dependencies and module names. So without this .opam file dune doesn’t build. What is meant when you mentiond that opam does the packaging and dune does the build ? Aren’t these both one and the same thing ? That is how I used to execute, run tests and package Java code.

  • Could you point out how dune generates this .opam file ? This is required by your Makefile

I am trying to understand the purpose of the 3 files dune-project, .opam and dune. Looks like .opam has the build lifecycle instructions like Apache Maven’s pom.xml

Thanks,
Mohan

Packaging is about software deployment, which is to say making a software system available for use. Building is about constructing programs that can be used from source code (in conjunction with whatever dependencies). These two things are related, but different. E.g., interpreted language like Python have package managers, but (arguably) there is no “building”, you just install new source code and run it. On the other extreme, binary package managers install pre-built artifacts, so while building is required to create the package, the package manager itself doesn’t involve a build phase. In source based package mangers, the border is easily blurred, since deploying some software means getting the source code onto the target machine and building it. It is common for language-specific tooling to combine source based package management and the build system into one too (or at least to wrap this for end users into one tool), but they are logically distinct areas of concern.

If you look in your .opam files, you will see see an entry like build: ["dune" "build" ...]. This tells opam ow to build the source of the package. It uses this instruction as part of its installation process. You could replace this with another build tool, like make or ocamlbuild or with just an invocation of ocamlc directly! But standard practice is now generally to use dune. (You could also package up ocaml code using a different package manager, like nix or pacman, in which case you might use dune but not opam).

I suspect this isn’t true: if you remove the .opam file in question dune will probably still build fine, assuming all the dependencies are already installed. What opam files are good for, however, is providing a portable definition of all the ocaml dependencies needed by a package. This lets you pull down the source code onto a new machine (whether real or virtual), and just do opam install . to have all the dependencies installed and the package built. (This is just a special case of “configuration as code”.)

See https://dune.readthedocs.io/en/stable/dune-files.html#generate-opam-files and https://dune.readthedocs.io/en/stable/opam.html#generating-opam-files for how to configure dune to generate opam files. Let me know if you have any followup questions about making use of that configuration.

I’m not familiar with Java land, so I’m afraid I can’t help with comparison. But I can explain how these three files relate:

  • .opam species a package for opam. A package includes some meta data about the project (like its name, the author, etc.) as well as a list of other opam packages required as dependencies. It also includes instructions on how to build and install the backage.
  • dune-project specifies a software project that will be built with dune. A project can consist of one or more software components (libraries, tests, executables, data). It only supports a few pieces of meta data, and was recently extended to also enable generation of .opam files. If you enable this (as described above), you won’t need to think too much about the .opam file any more. See https://dune.readthedocs.io/en/stable/dune-files.html#dune-project for documentation on this file.
  • dune files specify components that can be built by dune. They describe the libraries that a component relies on, any preprocessors it uses, what executable or library it builds to, etc. See https://dune.readthedocs.io/en/stable/dune-files.html#dune for documentation on dune files.

The reason for the distinction between opam dependencies and library dependencies in a dune file can be understood by considering this kind of example: an executable and library might both be defined in the same package, but the don’t necessarily share the same dependencies. The executable may need a CLI parsing library, but the internal library in the package won’t require this, so we’ll end up with the CLI parsering lib in the .opam file, but it will only show up in the dune file for the executable, not for the internal library.

If we started over from scratch, we’d probably have fewer moving parts to this whole system, but thankfully they are all converging into a nicely integrated set of tools, and things are improving all the time.