Если вы выбросили System.Exception, то вызывающая сторона должна перехватить System.Exception. Обе стороны этого - нет-нет, так как это заставляет нас говорить пользователю «это не сработало», а не что-то более полезное.
ArgumentException полезно, если вызывающая сторона передала недопустимый аргумент. Если у вашей функции есть параметр int, который должен быть четным числом, вы бы выдавали ArgumentException и сообщали вызывающей стороне, какой параметр недопустим и почему. Однако, если все аргументы были действительны, но проблема все еще существует, вам, вероятно, понадобится пользовательское исключение. Таким образом, вызывающий абонент может точно сказать пользователю, что пошло не так.
Тест для меня действительно на стороне вызова. Если бы у меня было дюжина уловов, которые делали бы одно и то же, то мне действительно нужно было только одно исключение. Однако, если бы у меня был один улов и было бы заявление, сообщающее пользователю, что одна из трех вещей пошла не так, то мне действительно нужно три уникальных исключения.