我正在尝试用C#为静态方法编写单元测试
本文关键字:静态方法 单元测试 | 更新日期: 2023-09-27 18:04:29
我正在学习c#web应用程序的单元测试。我陷入了上述情景。我不确定我做这件事的方式是否正确。我有用于单元测试的FakePath类。如何在MSTest中为静态方法Abc.log((编写单元测试?
public class Abc
{
public static void log(string msg)
{
//Read on Write on path;
string path = getPath(new ServerPath());
}
public static string getPath(IServerPath path)
{
return path.MapPath("file.txt");
}
}
interface IServerPath()
{
string MapPath(string file);
}
class ServerPath : IServerPath
{
string MapPath(string file)
{
return HttpContext.Current.Server.MapPath(file);
}
}
class FakeServerPath : IServerPath
{
string MapPath(string file)
{
return @"C:'"+file;
}
}
您正在尝试测试一个void方法,因此断言该方法的选项之一是验证该方法是否被调用:
string expectedStr = "c:'file.txt";
[TestMethod]
public void FakeServerPath_VerifyMapPathWasCalled()
{
var fakeServerPath = Isolate.Fake.NextInstance<ServerPath>();
Isolate.WhenCalled(() => fakeServerPath.MapPath("")).WillReturn(expectedStr);
Abc.log("");
Isolate.Verify.WasCalledWithExactArguments(() => fakeServerPath.MapPath("file.txt"));
}
另一个选项是测试getPath(IServerPath path)
方法的返回值,方法是修改ServerPath's
MapPath(string file)
方法的返回值来返回所需值,并断言返回值是否如预期。
string expectedStr = "c:'file.txt";
[TestMethod]
public void ModifyReturnValueFromMapPath_IsEqualToExpactedStr()
{
var fakeServerPath = Isolate.Fake.NextInstance<ServerPath>();
Isolate.WhenCalled(() => fakeServerPath.MapPath("")).WillReturn(expectedStr);
var result = Abc.getPath(fakeServerPath);
Assert.AreEqual(expectedStr, result);
}
请注意,通过使用TypeMock Isolator,您将能够在不更改原始代码的情况下伪造"ServerPath"的未来实例。如果必要的话,TypeMock也可以这样模拟HttpContext
类:
string expectedStr = "c:'file.txt";
[TestMethod]
public void ModifyReturnValueFromHttpContext_IsEqualToExpactedStr()
{
var serverPath = new ServerPath();
Isolate.WhenCalled(() => HttpContext.Current.Server.MapPath("")).WillReturn(expectedStr);
var result = Abc.getPath(serverPath);
Assert.AreEqual(expectedStr, result);
}
测试静态本身很容易,只需调用它们并对结果进行断言(实际问题是测试使用静态的代码,因为它们无法模拟出来(。这种特殊情况下的测试因另一件事而变得复杂。
Log
方法的真正问题是ti自己创建了一个ServerPath
实例,这排除了任何依赖注入的机会(相反,GetPath
方法对测试完全友好,因为它将接口作为参数(。
我将对Abc
类进行重构,以便对其进行更好的测试
public static class Logger
{
public static void Log(IServerPath path, string msg)
{
//add parameter checking here
string path = path.MapPath("file.txt");
//actual log goes here
}
}
请注意,现在测试将负责创建IServerPath
实例,然后可以使用该实例注入模拟
在这种情况下,您需要公开一种设置依赖关系的方法。目前,您在方法中直接使用new ServerPath()
,这使得注入FakeServerPath
进行测试变得困难。
您可以修改Abc
public class Abc
{
static Abc() { ServerPath = new ServerPath(); }
public static IServerPath ServerPath { get; set; }
public static void log(string msg) {
//Read on Write on path;
string path = getPath(ServerPath);
}
public static string getPath(IServerPath path) {
return path.MapPath("file.txt");
}
}
测试可能看起来像
[TestMethod]
public void Abc_log_Test() {
//Arrange
string filename = "fakeFile.txt";
string expected = @"C:'" + filename;
var mockServerPath = new Mock<IServerPath>();
mockServerPath
.Setup(m => m.MapPath(filename))
.Returns(expected)
.Verifiable();
Abc.ServerPath = mockServerPath.Object;
var message = "Hello world";
//Act
Abc.log(message);
//Assert
mockServerPath.Verify();
}
注意,我使用Moq来模拟服务器路径
这里有一个使用依赖注入的简单方法。
public class FileLogger
{
private readonly string _filePath;
public FileLogger(string filePath)
{
_filePath = filePath;
}
public void Log(string msg)
{
//write to the log
}
}
现在,您的日志记录类只有一个职责——写入文件。它不是负责确定要写入哪个文件。它希望将该值注入其中。
此外,我避免使用static
方法。如果方法是static
,那么类似的问题再次出现:如何测试依赖于日志记录类的类?
通过使其非静态,您可以重复相同的模式-模拟记录器,这样您就可以测试依赖它的类
public interface ILogger
{
void Log(string msg);
}
然后,日志记录类可以实现该接口,并且可以将该接口(而不是具体类(注入可能需要写入日志的类中。
下面是一篇演示将记录器注入类的文章。通常有更好的方法来实现相同的目的(比如使用拦截器(,但至少它可以防止代码在任何地方都依赖于某个具体类。