如何为字典 C# 创建单元测试
How to create a unit test for dictionary C#
我想制作一个rpn计算器并使用nunit为字典创建单元测试来测试操作,但我不知道如何制作。
static Stack<double> stack { get; set; } = new Stack<double>();
static Dictionary<string, Action> operators = new Dictionary<string, Action>
{
["+"] = () => { stack.Push(stack.Pop() + stack.Pop()); },
["-"] = () => { var x = stack.Pop(); stack.Push(stack.Pop() - x); },
["*"] = () => { stack.Push(stack.Pop() * stack.Pop()); },
["/"] = () => { var x = stack.Pop(); if (x == 0) throw new DivideByZeroException(); stack.Push(stack.Pop() / x); },
["clr"] = () => { stack.Clear(); },
["!"] = () => { var x = stack.Pop(); stack.Push(x == 0 ? 1 : 0); },
["!="] = () => { stack.Push(stack.Pop() == stack.Pop() ? 0 : 1); },
["%"] = () => { var x = stack.Pop(); stack.Push(stack.Pop() % x); },
["++"] = () => { var x = stack.Pop(); x++; stack.Push(x); },
["--"] = () => { var x = stack.Pop(); x--; stack.Push(x); },
}
编辑:执行就像
while (true)
{
Display();
var readLine = Console.ReadLine();
var tokens = readLine.Split(" ").Where(t => t != string.Empty).ToArray();
foreach (var token in tokens)
{
try
{
operators[token].Invoke();
}
catch(KeyNotFoundException)
{
stack.Push(double.Parse(token));
}
}
}
我认为您应该将 stack
和 operators
作为 private
字段放入某些 class
中,例如 CalculatingService
。之后你应该创建 public
方法 Calculate(...)
,它将 return
计算值:
public class CalculatingService : ICalculatingService
{
private readonly Stack<double> stack { get; set; }
private readonly Dictionary<string, Func<double>> operators;
public CalculatingService()
{
stack = new Stack<double>();
InitDictionary();
}
public double Calculate(string @operator) =>
operators[@operator].Invoke();
public void ClearData() => stack.Clear();
private void InitDictionary() => operators = new Dictionary<string, Func<double>>
{
["+"] = () => stack.Pop() + stack.Pop(),
["-"] = () => { var x = stack.Pop(); return stack.Pop() - x; },
["*"] = () => stack.Pop() * stack.Pop(),
["/"] = () => { var x = stack.Pop(); if (x == 0) throw new DivideByZeroException(); return stack.Pop() / x; },
["!"] = () => { var x = stack.Pop(); return x == 0 ? 1 : 0; },
["!="] = () => stack.Pop() == stack.Pop() ? 0 : 1,
["%"] = () => { var x = stack.Pop(); return stack.Pop() % x; },
["++"] = () => { var x = stack.Pop(); x++; return x; },
["--"] = () => { var x = stack.Pop(); x--; return x; }
};
}
现在您可以为 CalculatingService
中的所有方法创建测试方法。您可以通过为 x 个运算符编写 x 个测试方法来测试 Calculate(...)
。我不知道,你如何管理 stack
- 你必须在 CalculatingService
中为 stack
管理编写额外的 public
方法,就像我写的 ClearData()
.
我会先做 non-static。否则每个测试都会影响下一个,除非你清除堆栈。如果每次测试都可以 calculator = new Calculator()
就更容易了。
从外观上看,操作是作为字符串发送到计算器的。它没有显示,但我猜数字也是。不清楚。
为了确保您的计算器正常工作,您可能希望向其发送一些数字和运算,并确保计算器完成后计算结果正确。一种方法是使用参数化测试。也就是说,您编写了一个测试,但向它发送了多组输入。
这是一个 NUnit 示例。请记住,我不知道你的计算器如何工作的确切细节,所以这更像是一个指针,而不是一个完美的例子。
首先我必须创建一个基本的 Calculator
class,因为使用静态 class 编写单元测试要困难得多。 (等等,你的堆栈和字典不见了!更多相关信息。)
public class Calculator
{
public string DisplayedResult { get; }
public void SendInput(string input)
{
}
}
这个想法是 SendInput
就像按下一个按钮,当你按下一个按钮时 DisplayedResult
会发生变化。因此,如果您按“2 + 2 2 =”,它将显示“24”。如果您按“5 / 0 =”,它可能会显示“错误”。如果有一个“清除”按钮,那么“5 / 0 = C 2 + 2 =”可能会显示“4”。 (先有错误,后被清除,然后又运行了一次。)
现在我们可以像这样编写一个参数化的 NUnit 测试:
[Test]
[TestCase("2 + 2 2 =","24")]
[TestCase("5 / 0 =", "error")]
[TestCase("5 / 0 = C 2 + 2 =", "4")]
public void Calculator_Displays_Expected_Results(string input, string expectedResult)
{
var inputsToSend = input.Split(' ');
var calculator = new Calculator();
foreach (var inputToSend in inputsToSend)
{
calculator.SendInput(inputToSend);
}
Assert.AreEqual(expectedResult, calculator.DisplayedResult);
}
每个测试用例都有一个输入——一系列由字符串分隔的操作。这使得创建每个测试用例变得容易。它也有一个预期的结果。我们希望计算器显示什么?
测试发送输入并验证您是否获得预期结果。您可以将它分成多个测试,或者您可以进行一个包含大量案例的大型测试。您可以根据需要编写任意数量的测试。您可以添加另一个参数来描述正在测试的内容。
两个重要细节:
设计必须考虑测试。静态 classes 更难测试。此外,更容易测试 class 是否公开了一个易于测试的接口 - 输入和输出。如果某件事难以测试,则通常表示设计存在困难。所以我们会调整到更容易测试的设计是正常的。
字典和堆栈呢?如果这就是您想要实现计算器的方式,那很好 - 您可以添加它。也许 SendInput
方法与它们交互并根据需要更新 DisplayedResult
。
重要的是测试不会与该实施细节相关联。如果您改变主意,不使用堆栈和字典,而是编写完全不同的代码来处理这些操作怎么办?这些测试仍然完全有效。只要您的实施有效,无论是什么,测试都会通过。
我想制作一个rpn计算器并使用nunit为字典创建单元测试来测试操作,但我不知道如何制作。
static Stack<double> stack { get; set; } = new Stack<double>();
static Dictionary<string, Action> operators = new Dictionary<string, Action>
{
["+"] = () => { stack.Push(stack.Pop() + stack.Pop()); },
["-"] = () => { var x = stack.Pop(); stack.Push(stack.Pop() - x); },
["*"] = () => { stack.Push(stack.Pop() * stack.Pop()); },
["/"] = () => { var x = stack.Pop(); if (x == 0) throw new DivideByZeroException(); stack.Push(stack.Pop() / x); },
["clr"] = () => { stack.Clear(); },
["!"] = () => { var x = stack.Pop(); stack.Push(x == 0 ? 1 : 0); },
["!="] = () => { stack.Push(stack.Pop() == stack.Pop() ? 0 : 1); },
["%"] = () => { var x = stack.Pop(); stack.Push(stack.Pop() % x); },
["++"] = () => { var x = stack.Pop(); x++; stack.Push(x); },
["--"] = () => { var x = stack.Pop(); x--; stack.Push(x); },
}
编辑:执行就像
while (true)
{
Display();
var readLine = Console.ReadLine();
var tokens = readLine.Split(" ").Where(t => t != string.Empty).ToArray();
foreach (var token in tokens)
{
try
{
operators[token].Invoke();
}
catch(KeyNotFoundException)
{
stack.Push(double.Parse(token));
}
}
}
我认为您应该将 stack
和 operators
作为 private
字段放入某些 class
中,例如 CalculatingService
。之后你应该创建 public
方法 Calculate(...)
,它将 return
计算值:
public class CalculatingService : ICalculatingService
{
private readonly Stack<double> stack { get; set; }
private readonly Dictionary<string, Func<double>> operators;
public CalculatingService()
{
stack = new Stack<double>();
InitDictionary();
}
public double Calculate(string @operator) =>
operators[@operator].Invoke();
public void ClearData() => stack.Clear();
private void InitDictionary() => operators = new Dictionary<string, Func<double>>
{
["+"] = () => stack.Pop() + stack.Pop(),
["-"] = () => { var x = stack.Pop(); return stack.Pop() - x; },
["*"] = () => stack.Pop() * stack.Pop(),
["/"] = () => { var x = stack.Pop(); if (x == 0) throw new DivideByZeroException(); return stack.Pop() / x; },
["!"] = () => { var x = stack.Pop(); return x == 0 ? 1 : 0; },
["!="] = () => stack.Pop() == stack.Pop() ? 0 : 1,
["%"] = () => { var x = stack.Pop(); return stack.Pop() % x; },
["++"] = () => { var x = stack.Pop(); x++; return x; },
["--"] = () => { var x = stack.Pop(); x--; return x; }
};
}
现在您可以为 CalculatingService
中的所有方法创建测试方法。您可以通过为 x 个运算符编写 x 个测试方法来测试 Calculate(...)
。我不知道,你如何管理 stack
- 你必须在 CalculatingService
中为 stack
管理编写额外的 public
方法,就像我写的 ClearData()
.
我会先做 non-static。否则每个测试都会影响下一个,除非你清除堆栈。如果每次测试都可以 calculator = new Calculator()
就更容易了。
从外观上看,操作是作为字符串发送到计算器的。它没有显示,但我猜数字也是。不清楚。
为了确保您的计算器正常工作,您可能希望向其发送一些数字和运算,并确保计算器完成后计算结果正确。一种方法是使用参数化测试。也就是说,您编写了一个测试,但向它发送了多组输入。
这是一个 NUnit 示例。请记住,我不知道你的计算器如何工作的确切细节,所以这更像是一个指针,而不是一个完美的例子。
首先我必须创建一个基本的 Calculator
class,因为使用静态 class 编写单元测试要困难得多。 (等等,你的堆栈和字典不见了!更多相关信息。)
public class Calculator
{
public string DisplayedResult { get; }
public void SendInput(string input)
{
}
}
这个想法是 SendInput
就像按下一个按钮,当你按下一个按钮时 DisplayedResult
会发生变化。因此,如果您按“2 + 2 2 =”,它将显示“24”。如果您按“5 / 0 =”,它可能会显示“错误”。如果有一个“清除”按钮,那么“5 / 0 = C 2 + 2 =”可能会显示“4”。 (先有错误,后被清除,然后又运行了一次。)
现在我们可以像这样编写一个参数化的 NUnit 测试:
[Test]
[TestCase("2 + 2 2 =","24")]
[TestCase("5 / 0 =", "error")]
[TestCase("5 / 0 = C 2 + 2 =", "4")]
public void Calculator_Displays_Expected_Results(string input, string expectedResult)
{
var inputsToSend = input.Split(' ');
var calculator = new Calculator();
foreach (var inputToSend in inputsToSend)
{
calculator.SendInput(inputToSend);
}
Assert.AreEqual(expectedResult, calculator.DisplayedResult);
}
每个测试用例都有一个输入——一系列由字符串分隔的操作。这使得创建每个测试用例变得容易。它也有一个预期的结果。我们希望计算器显示什么?
测试发送输入并验证您是否获得预期结果。您可以将它分成多个测试,或者您可以进行一个包含大量案例的大型测试。您可以根据需要编写任意数量的测试。您可以添加另一个参数来描述正在测试的内容。
两个重要细节:
设计必须考虑测试。静态 classes 更难测试。此外,更容易测试 class 是否公开了一个易于测试的接口 - 输入和输出。如果某件事难以测试,则通常表示设计存在困难。所以我们会调整到更容易测试的设计是正常的。
字典和堆栈呢?如果这就是您想要实现计算器的方式,那很好 - 您可以添加它。也许 SendInput
方法与它们交互并根据需要更新 DisplayedResult
。
重要的是测试不会与该实施细节相关联。如果您改变主意,不使用堆栈和字典,而是编写完全不同的代码来处理这些操作怎么办?这些测试仍然完全有效。只要您的实施有效,无论是什么,测试都会通过。