If you have specific hardware requirements for your project, or you are developing on one hardware platform and need to target another like Windows vs MacOS, you will need to use a virtual machine. Most other 'software only' requirements can be met by using containers.
It is entirely possible to use containers and virtual machines in unison although the practical use-cases may be limited. A virtual machine can be created that emulates a unique hardware configuration. An operating system can then be installed within this virtual machine's hardware. Once the virtual machine is functional and boots the operating system, a container runtime can be installed on the operating system. At this point we have a functional computational system with emulated hardware that we can install containers on.
One practical use for this configuration is experimentation for system on chip deployments. Popular system on chip computational devices like the Raspberry Pi, or BeagleBone development boards can be emulated as a virtual machine, to experiment with running containers on them before testing on the actual hardware.
But the majority of the time, your needs will likely be met by one of the two. The key to deciding between containers or virtual machines for your virtualization needs is understanding your resource needs and the trade-offs you’re willing to make.