我有一个方法从另一个类调用另一个方法,该方法又在第二个类中执行许多私有方法。一种方法写入一个文件然后由外部进程拾取,外部进程运行5到30分钟,具体取决于处理的需要,然后生成另一个文件,由我的应用程序读取,然后将其读取并返回到被称为初始方法。
我知道我描述的不是"单位"但是这个方法是公开的,所以我的问题是我需要在这个方法中测试什么?如何在第二个类中模拟对方法的调用?或者我只是让方法正常运行,无论是5分钟还是30分钟?
class A
{
public List<DataClass> MethodUnderTest()
{
List<string> requiredData;
SecondClass B = New SecondClass();
requiredData = B.GenerateFile();
//B.GenerateFile() Executes a number of private methods within the SecondClass,
//This can be treated as a service call. This runs between 5 and 30 mins
return requiredData.Select(r => new DataClass{
Property1 = r.Substring(0,2),
Property2 = r.Substring(3,5),
Property3 = r.Substring(9,10)
}).ToList();
}
}
答案 0 :(得分:1)
我认为你回答了自己的问题,但我将概述我会做什么:为SecondClass创建一个接口并依赖于它(你需要注入那个依赖(你喜欢的任何方式)),然后我会嘲笑它在单元测试中的行为。因为moq会做这个工作。这给我们带来了什么样的逻辑来测试:唯一有趣的细节是子串的东西 - 所以测试一下。然后,您将需要访问SecondClass并查看您可以在那里创建的单元测试。
单位和非单位测试怎么样?我说你需要两者,但你会不同地执行它们(每次更改机器上的代码时都应运行单元(因此,它们需要快速执行(因此模拟是关键),如ms中的几k),以及将在提交时执行所有依赖项的集成/功能测试(通常在构建服务器上)。并且它们将花费更多时间(在您的情况下至少30秒到几米)但这很好,因为它们不经常运行
答案 1 :(得分:1)
将SecondClass
视为第三方依赖,并将其封装在您控制的代码之后。从依赖项创建所需功能的抽象;
public interface ISecondClass {
List<string> GenerateFile();
}
A类也有太多顾虑,应该删除任何不属于A类的责任/顾虑。
public interface IDataClassParser {
DataClass Parse(string data);
}
public class DefaultDataClassParser : IDataClassParser {
public DataClass Parse(string data) {
return new DataClass {
Property1 = data.Substring(0, 2),
Property2 = data.Substring(3, 5),
Property3 = data.Substring(9, 10)
};
}
}
以上是用于演示目的的简单示例。
将目标类重构为现在显式依赖于抽象,而不是结果。
public class A {
private readonly ISecondClass B;
private readonly IDataClassParser parser;
public A(ISecondClass B, IDataClassParser parser) {
this.B = B;
this.parser = parser;
}
public List<DataClass> MethodUnderTest() {
List<string> requiredData = B.GenerateFile();
return requiredData.Select(createNewDataClass).ToList();
}
private DataClass createNewDataClass(string r) {
return parser.Parse(r);
}
}
类A
不再与实现问题紧密耦合,现在可以单独测试被测方法。
示例测试
[TestClass]
public class ATest {
[TestMethod]
public void MethodUnderTest_Should_Return_DataClassList() {
//Arrange
List<string> mockData = new List<string>();
//TODO: Populate mockData
var mockB = new Mock<ISecondClass>();
mockB.Setup(_ => _.GenerateFile()).Returns(mockData);
var sut = new A(mockB.Object, new DefaultDataClassParser());
//Act
var actual = sut.MethodUnderTest();
//Assert
//TODO: assert that the actual result satisfies expectations
}
}
从技术上讲,上面现在只是实际测试解析器,因此可以编写一个额外的测试来单独测试解析代码。
[TestClass]
public class DataClassParserTest {
[TestMethod]
public void DataClassParser_Should_Return_DataClass() {
//Arrange
string mockData = "..."; //TODO: Populate mockData
var sut = new DefaultDataClassParser();
//Act
var actual = sut.Parse(mockData);
//Assert
//TODO: assert that the actual result satisfies expectations
}
}
最后,在生产中,长期运行的类的实现将来自抽象并封装依赖SecondClass
public class SecondClassWrapper : ISecondClass {
private SecondClass B = new SecondClass();
public List<string> GenerateFile() {
return B.GenerateFile();
}
}
答案 2 :(得分:1)
您正在尝试确定如何对调用另一个类(A
)的方法的类(SecondClass
)的方法进行单元测试。您正在测试的方法并不多。在大多数情况下,如果它调用的方法有效,它将起作用。因此(GenerateFile
)是专注于编写测试的最佳位置。
您提到SecondClass
会调用许多私有方法。这些方法有多复杂?我猜测(我可能会离开)其中一些相当复杂,因为调用它们的方法需要很长时间才能运行。如果测试构成该长期运行过程的小任务,则您的过程是经过测试的方法或类的组合。
如果这些私有方法很复杂,那么您无法通过测试调用调用这些方法的方法的方法来测试它们。也许这些私有方法的某些行为可以放在单独的类中,这些类本身可以进行单元测试。
听起来好像很多东西,但为了获得测试的好处,我们必须编写可以测试的代码。这改变了我们编写代码的方式,通常情况下会更好。
回到您最初询问的方法:
您有一个方法需要List<string>
并将其转换为List<DataClass>
。为简单起见,为什么不编写一个将string
转换为DataClass
的方法?也许你可以把它放在一个扩展中,甚至可以放在它自己的类中。
DataClass FromString(string input)
{
return new DataClass{
Property1 = r.Substring(0,2),
Property2 = r.Substring(3,5),
Property3 = r.Substring(9,10)
}
}
然后,您可以测试该方法,以确保从字符串解析的DataClass
具有预期的属性值。而你之前的陈述变得如此简单,甚至几乎不需要对其自身进行测试。
return requiredData.Select(r => FromString(r)).ToList();
正如另一个答案中所建议的那样,SecondClass
理想情况下应该是A
所依赖的界面。但即使它不是,如果SecondClass
本身已经过单元测试,那么MethodUnderTest
将是合理的,因为它除了调用一个测试方法并将结果传递给另一种测试方法。
但是如果你用接口取代SecondClass
,那么MethodUnderTest
变得非常容易测试,因为你只需要使用&#34; test double&#34;这是一个简单的实现,仅用于测试目的& #39; s硬编码返回您要测试的值。因此,您的测试有效地说,&#34;假设ISecondClass
返回这些值,我希望MethodUnderTest
返回这些值。&#34;它不会花费几分钟来运行,只需几毫秒。
即使所有部分都经过测试,您仍然需要进行端到端的整个测试的集成测试。你能创建一个包含较少数据的小文件并对其进行测试吗?