Virtualization is by far the simplest.
However you have 2 seperate use cases here, which will have different solutions
1. Try out new distro's
Distributions are basically determined by the packaged applications, and userspace environment (e.g.
init for boot)
If you want to "evaluate" the UIX of a different distribution, qualitatively, then I would recommend full-blown virtualization where you install the OS in its entirety and evaluate its usability. This is covered adequately in other answers.
If you simply need the userspace environment for testing then read on.
2. Testing and "throw-away instances" in different environments
It is easier, cheaper, and faster to use containerization, a form of light weight virtualization that uses the kernel to create sandboxed environments.
A container shares kernel resources with the Host, but otherwise has its own root file system, userspace, network stack, etc. It can be thought of, conceptually as a
chroot on steroids. However, because the kernel is shared the virtualization is "thin", meaning that for most practical purposes it runs at the same speed as the host OS.
There is commonly used container system called
docker. Docker has standardized images for practically every linux distribution you would like, and it runs on windows (however, windows images only work on windows, linux images work on both) . It has additional useful features to save space and performance.
There are also native open source alternatives for linux like
LXC (which is built into the kernel!) , which can be used to much the same thing (but with more configuration required).
Simplified Example of a testing or build environment in
RUN apt-get update && apt-get install -y build-essential
docker build --tag my-builder .
Then from the command line, compile your project or tests in that environment in a variety of ways
"login" and compile within the environment, run tests etc. Assuming you are in source directory of your project
$ docker run -v "$PWD:/workdir" --rm -it my-builder /bin/bash
# echo "Now in docker container"
$ echo "Build artifacts are now on your host OS Directory :) "
Use as one-off
$ docker run -v "$PWD:/workdir" --rm my-builder make
You can even pass environment variables
$ docker run -e "CROSS_COMPILE=arm-linux-gnueabi" -v "$PWD:/workdir" --rm my-builder make
Or start a persistent instance and copy files into it explicitly
$ Start our instance in background
$ docker run --name my-builder-inst -d my-builder
$ echo "Copy files to instance"
$ docker cp /my/source/dir my-builder-inst:/workdir
$ echo "run project build"
$ docker exec my-builder-inst make
$ echo "copy build artifacts"
$ docker cp my-builder-inst:/workdir/build /my/output/dir
$ echo "destroy and delete container"
$ docker rm -f my-builder-inst
There are literally hundreds of other use patterns, however, the script-like image definition, extendable images, and command line use makes it extremely attractive for development, test, and even deployment environments