#c_sharp #юнит_тесты #атрибуты #nunit
Есть несколько однотипных юнит-тестов nUnit, в которых проверяется парсинг строки в decimal (именно этот тип рекомендуется в c# для денежных единиц): [Test] public void Test3() { // Arrange var input = "1,234.56"; // Act var result = MoneyParser.Parse(input); // Assert Assert.AreEqual(1234.56m, result); } Ну и я соответственно захотел всё это элегантно упаковать в один метод навесив атрибут TestCase: [TestCase("1234", 1234m)] [TestCase("1234.56", 1234.56m)] [TestCase("1234,56", 1234.56m)] [TestCase("1 234.56", 1234.56m)] [TestCase("1 234,56", 1234.56m)] [TestCase("1,234.56", 1234.56m)] public void Test(string input, decimal expected) { var result = MoneyParser.Parse(input); Assert.AreEqual(expected, result); } Однако в атрибутах можно использовать только примитивные типы, а код с decimal даже не компилируется — выдаёт ошибку: Error CS0182 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type Как можно обойти это препятствие и написать читаемый тест? На ум приходит только вариант задавать на входе expected строкой, преобразовывать внутри теста - и это не нравится ни тем, что легко поломать текст невалидными данными (нет типобезопасностм), ни тем, что придётся загромождать код лишними преобразованиями.
Ответы
Ответ 1
Поскольку в C# тип double может неявно преобразовываться в тип decimal, можно просто убрать постфикс m для всех decimal-значений в атрибуте TestCase. Потерь по точности в данном случае не будет (это можно легко проверить, если нужно). В конечном итоге тест будет выглядеть примерно так: [TestFixture] public class MoneyParserTests { [TestCase("1234", 1234)] [TestCase("1234.56", 1234.56)] [TestCase("1234,56", 1234.56)] [TestCase("1 234.56", 1234.56)] [TestCase("1 234,56", 1234.56)] [TestCase("1,234.56", 1234.56)] public void Test(string input, decimal expected) { var result = MoneyParser.Parse(input); Assert.AreEqual(expected, result); } } Единственный минус: глядя на эти тесткейсы не скажешь, что метод принимает именно decimal параметры. В других случаях Unit-тест будет обрастать лишними проверками, явными преобразованиями и т.д.Ответ 2
Можно попробовать передать тесты не через атрибуты, с помощью TestCaseSource. Получится страшненько, но сработает: private static readonly Tuple[] cases = new[] { Tuple.Create("1234", 1234m), Tuple.Create("1234.56", 1234.56m), Tuple.Create("1234,56", 1234.56m), Tuple.Create("1 234.56", 1234.56m), Tuple.Create("1 234,56", 1234.56m), Tuple.Create("1,234.56", 1234.56m) }; private static object[] TestCases() { return cases.Select(t => new object[] { t.Item1, t.Item2 }).ToArray(); } [TestCaseSource(nameof(TestCases))] public void Test(string input, decimal expected) { var result = MoneyParser.Parse(input); Assert.AreEqual(expected, result); } Преимущество: до определенной степени сохранена типобезопасность при изменении тестов. Недостатки: читаемость упала; из-за сигнатуры object[] сломать код достаточно легко; появился код для преобразования данных.
Комментариев нет:
Отправить комментарий