我有一个void方法,我想对其进行单元测试,但是不幸的是,我对此没有太多经验。
...
这是我要在其中测试的TestClass,但是在运行它时它会不断加载:
taskrole
答案 0 :(得分:4)
对这种逻辑进行编码和单元测试的正确方法是:
public class SomeProductionClass
{
public string findOccuranceMethod(string str)
{
string occurString = "o";
string replaceString = "MDDS";
var array = str.Split(new[] { occurString }, StringSplitOptions.None);
string result = string.Join(replaceString, array);
return result;
}
}
[TestMethod()]
public void findOccuranceMethodTest()
{
// Arrange
string expected = "The Haunting MDDSf Hill HMDDSuse!";
var productionClass = new SomeProductionClass();
// Act
var result = productionClass.findOccuranceMethod("The Haunting of Hill House!");
// Assert
Assert.AreEqual(expected, result);
}
如果由于某种原因您无法控制生产代码,则有两种选择:
用抽象替换Console
将标准输入/输出重新分配给自定义流:这是最不推荐使用的选项,因为它会使设置/清理更加复杂,并且会干扰测试运行器。
在任何一种情况下,我们都决不能以会为测试/生产引入单独分支的方式修改被测代码。例如,“如果在测试中运行,则执行A,否则执行B” –在这种情况下,我们实际上不是在测试生产代码,而是测试它的另一个分支。
在这里,我们介绍IConsole
来代替System.Console
:
public interface IConsole
{
void WriteLine(string s);
void ReadLine(); // void is enough for this example
}
// for use in production
public class RealConsole : IConsole
{
public void WriteLine(string s)
{
Console.WriteLine(s);
}
public void ReadLine()
{
Console.ReadLine();
}
}
// for use in unit tests
public class TestConsole : IConsole
{
public void WriteLine(string s)
{
Contents.Add(s);
}
public void ReadLine()
{
}
public List<string> Contents { get; } = new List<string>();
}
生产代码将保留原始帖子中的内容,只不过现在它使用_console
作为依赖项:
public class SomeProductionClass
{
private readonly IConsole _console;
public SomeProductionClass(IConsole console)
{
_console = console;
}
public void findOccuranceMethod()
{
string str = "The Haunting of Hill House!";
_console.WriteLine("String: " + str);
string occurString = "o";
string replaceString = "MDDS";
var array = str.Split(new[] { occurString }, StringSplitOptions.None);
var count = array.Length - 1;
string result = string.Join(replaceString, array);
_console.WriteLine("String after replacing a character: " + result);
_console.WriteLine("Number of replaces made: " + count);
_console.ReadLine();
}
}
,测试代码为:
[TestMethod()]
public void findOccuranceMethodTest()
{
// Arrange
string expectedString = "The Haunting MDDSf Hill HMDDSuse!";
int expectedCount = 2;
var console = new TestConsole();
var productionClass = new SomeProductionClass(console);
// Act
productionClass.findOccuranceMethod();
// Assert
Assert.AreEqual(3, console.Contents.Count);
Assert.AreEqual("String: The Haunting of Hill House!", console.Contents[0]);
Assert.AreEqual(
$"String after replacing a character: {expectedString}",
console.Contents[1]);
Assert.AreEqual(
$"Number of replaces made: {expectedCount}",
console.Contents[2]);
}
答案 1 :(得分:1)
如果您需要保留签名,可以使用可选参数:
public void optionSameSignature(Action<string> log = null, Func<string> read = null)
{
log = log ?? Console.WriteLine;
read = read ?? Console.ReadLine;
string str = "The Haunting of Hill House!";
log("String: " + str);
string occurString = "o";
string replaceString = "MDDS";
var array = str.Split(new[] { occurString }, StringSplitOptions.None);
var count = array.Length - 1;
string result = string.Join(replaceString, array);
log("String after replacing a character: " + result);
log("Number of replaces made: " + count);
read();
}
public void Test()
{
List<string> lines = new List<string>();
optionSameSignature(lines.Add, () => "");
}
从更深层次上讲,输入和输出是技术细节,因此理想情况下,您将尝试使用没有技术细节的业务价值代码。这样的事情可能更接近您的实际需求:
private static void mybusinessfunction(string input, out int count, out string result)
{
string occurString = "o";
string replaceString = "MDDS";
var array = input.Split(new[] { occurString }, StringSplitOptions.None);
count = array.Length - 1;
result = string.Join(replaceString, array);
}