Question: How to start writing code with TDD?
We start with the fact that we will solve one small problem. We will describe the business requirement in code using a test.
public class ReporterTests
{
[Fact]
public void ReturnNumberOfSentReports()
{
var reporter = new Reporter();
var reportCount = reporter.SendReports();
Assert.Equal(2, reportCount);
}
}
* This source code was highlighted with Source Code Highlighter .
Question: First we need to write a lot of tests, and then fix them one by one?
No, we go consistently. We will not disperse their efforts. At each time only one idle test. We will write the code that this test will fix and we can write the next test.
[Fact]
public void ReturnNumberOfSentReports()
{
IReportBuilder reportBuilder;
IReportSender reportSender;
var reporter = new Reporter(reportBuilder, reportSender);
var reportCount = reporter.SendReports();
Assert.Equal(2, reportCount);
}
* This source code was highlighted with Source Code Highlighter .
Question: Are there any rules for naming test methods?
Yes there is. It is desirable that the name of the test method indicates that it is testing the test and what result we expect. In this case, the name tells us: "The number of reports sent is returned."
Question: why it is worth using the IReportBuilder and IReportSender interfaces instead of creating specific classes?
You can implement the object for creating reports and the object for sending reports in different ways. It is now more convenient to hide the future implementations of these classes behind the interfaces .
Question: How to set the behavior of objects with which our test class interacts?
Instead of real objects with which our tested class interacts, it is most convenient to use stubs or mock objects. In the current application, we will create mock objects using the Moq library.
[Fact]
public void ReturnNumberOfSentReports()
{
var reportBuilder = new Mock<IReportBuilder>();
var reportSender = new Mock<IReportSender>();
// IReportBuilder
// : " CreateReports List<Report> 2 "
reportBuilder.Setup(m => m.CreateRegularReports())
.Returns( new List <Report> { new Report(), new Report()});
var reporter = new Reporter(reportBuilder.Object, reportSender.Object);
var reportCount = reporter.SendReports();
Assert.Equal(2, reportCount);
}
* This source code was highlighted with Source Code Highlighter .
public class Reporter
{
private readonly IReportBuilder reportBuilder;
private readonly IReportSender reportSender;
public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
{
this .reportBuilder = reportBuilder;
this .reportSender = reportSender;
}
public int SendReports()
{
return reportBuilder.CreateRegularReports().Count;
}
}
* This source code was highlighted with Source Code Highlighter .
Question: Is there a standard pattern for writing a test?
Yes. It is called Arrange-Act-Assert (AAA). Those. The test consists of three parts. Arrange (Set) - we make setting the input data for the test. Act (Act) - perform the action, the results of which are tested. Assert (Check) - check the execution results. I will sign the corresponding steps in the next test.
[Fact]
public void SendAllReports()
{
// arrange
var reportBuilder = new Mock<IReportBuilder>();
var reportSender = new Mock<IReportSender>();
reportBuilder.Setup(m => m.CreateRegularReports())
.Returns( new List <Report> { new Report(), new Report()});
var reporter = new Reporter(reportBuilder.Object, reportSender.Object);
// act
reporter.SendReports();
// assert
reportSender.Verify(m => m.Send(It.IsAny<Report>()), Times.Exactly(2));
}
* This source code was highlighted with Source Code Highlighter .
Question: Do I need to write tests for all objects of the application in one test class?
Very undesirable. In this case, the test class will grow to enormous size. It is best to create a separate test file for each class under test.
public int SendReports()
{
IList<Report> reports = reportBuilder.CreateRegularReports();
foreach (Report report in reports)
{
reportSender.Send(report);
}
return reports.Count;
}
* This source code was highlighted with Source Code Highlighter .
Question: How often should I run all the tests?
The more the better. Any change in the code may unexpectedly affect you in other parts of the system. Especially if this code was not written by you. Ideally, all tests should be run automatically by the integration system (Continuous Integration) during each build of the project.
Question: How to test private methods?
If you have read this far, you already understand that once the tests are written first and then the code, then all the code inside the class will be tested by default.
[Fact]
public void SendSpecialReportToAdministratorIfNoReportsCreated()
{
var reportBuilder = new Mock<IReportBuilder>();
var reportSender = new Mock<IReportSender>();
reportBuilder.Setup(m => m.CreateRegularReports()).Returns( new List <Report>());
reportBuilder.Setup(m => m.CreateSpecialReport()).Returns( new SpecialReport());
var reporter = new Reporter(reportBuilder.Object, reportSender.Object);
reporter.SendReports();
reportSender.Verify(m => m.Send(It.IsAny<Report>()), Times.Never());
reportSender.Verify(m => m.Send(It.IsAny<SpecialReport>()), Times.Once());
}
* This source code was highlighted with Source Code Highlighter .
public int SendReports()
{
IList<Report> reports = reportBuilder.CreateRegularReports();
if (reports.Count == 0)
{
reportSender.Send(reportBuilder.CreateSpecialReport());
}
foreach (Report report in reports)
{
reportSender.Send(report);
}
return reports.Count;
}
* This source code was highlighted with Source Code Highlighter .
Question: How to find out what code has already been tested?
Code coverage tests can be verified using various utilities. For a start, I can advise PartCover .
Question: Is it necessary to strive to cover the code with tests for 100%?
Not. This requires too much effort to create such tests and even more to support them. Normal cover ranges from 50 to 90%. Those. all business logic should be covered without accessing the database, external services and the file system.
Question: How can I test the interaction with the database, work with the SMTP server or the file system?
Indeed, it is necessary to test it. But this is not done by unit tests. Because the unit tests must pass quickly and not depend on external sources. Otherwise, you will run them once a week. For more details, see the article “Effective modular test” .
Q: When can I use TDD?
TDD can be used to create any application. It is very convenient to use it if you are studying the possibilities of a new library or programming language. There are no special limits in application. There may be inconvenience with testing multi-threaded and other specific applications.
Source: https://habr.com/ru/post/82842/
All Articles