我正在学习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;
}
}
答案 0 :(得分:2)
您正在尝试测试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);
}
答案 1 :(得分:0)
在这种情况下,您需要公开一种设置依赖关系的方法。目前,您在该方法中直接使用了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来模拟服务器路径
答案 2 :(得分:0)
测试静态本身很简单,只需调用它们并对结果进行断言(实际问题是测试使用静态的代码,因为它们无法被模拟掉)。在这种特殊情况下进行测试很复杂。
Log
方法的真正问题在于ti会自行创建ServerPath
个实例,从而排除了Dependency Injection的任何可能性(相反,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
实例,然后可以使用它来注入模拟
答案 3 :(得分:0)
这是一种使用依赖注入的简单方法。
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);
}
然后您的日志记录类可以实现该接口,您可以将该接口(而不是具体类)注入可能需要写入日志的类中。
Here's a post演示了如何将一个记录器注入一个类。通常有更好的方法来实现相同的目的(比如使用拦截器),但至少它会阻止你的代码在某个具体的类中到处都有依赖。