Skip to content

Commit d227706

Browse files
committed
Formatting Updates and WIP
Updating functional testing with 2.1 approach
1 parent f16eb30 commit d227706

File tree

3 files changed

+92
-98
lines changed

3 files changed

+92
-98
lines changed

docs/standard/microservices-architecture/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ms.workload:
1212
- "dotnet"
1313
- "dotnetcore"
1414
---
15+
1516
# Designing the infrastructure persistence layer
1617

1718
Data persistence components provide access to the data hosted within the boundaries of a microservice (that is, a microservice’s database). They contain the actual implementation of components such as repositories and [Unit of Work](http://martinfowler.com/eaaCatalog/unitOfWork.html) classes, like custom EF DBContexts.
@@ -103,7 +104,7 @@ We find repositories useful, but we acknowledge that they are not critical for y
103104

104105
The Specification pattern (its full name would be Query-specification pattern) is a Domain-Driven Design pattern designed as the place where you can put the definition of a query with optional sorting and paging logic.
105106

106-
The Specification pattern defines a query in an object. For example, in order to encapsulate a paged query that searches for some products, you can create a PagedProduct specification that takes the necessary input parameters (pageNumber, pageSize, filter, etc.). Then, within any Repository method (usually a List() overload) it would accept an ISpecification and run the expected query based on that specification.
107+
The Specification pattern defines a query in an object. For example, in order to encapsulate a paged query that searches for some products, you can create a PagedProduct specification that takes the necessary input parameters (pageNumber, pageSize, filter, etc.). Then, within any Repository method (usually a List() overload) it would accept an `ISpecification` and run the expected query based on that specification.
107108

108109
There are several benefits to this approach:
109110

@@ -113,10 +114,10 @@ There are several benefits to this approach:
113114

114115
* A specification can also be used to describe the shape of the data to be returned, so that queries can return just the data they required. This eliminates the need for lazy loading in web applications (which is usually not a good idea) and helps keep repository implementations from becoming cluttered with these details.
115116

116-
An example of a generic Specification interface is the following code from [eShopOnWeb](https://github.com/dotnet-architecture/eShopOnWeb ).
117+
An example of a generic Specification interface is the following code from [eShopOnWeb](https://github.com/dotnet-architecture/eShopOnWeb).
117118
118119
```csharp
119-
// https://github.com/dotnet-architecture/eShopOnWeb
120+
// https://github.com/dotnet-architecture/eShopOnWeb
120121
public interface ISpecification<T>
121122
{
122123
Expression<Func<T, bool>> Criteria { get; }
@@ -133,38 +134,37 @@ In the upcoming sections, it is explained how to implement the Specification pat
133134

134135
### The Repository pattern
135136

136-
- **Edward Hieatt and Rob Mee. Repository pattern.**
137-
[*http://martinfowler.com/eaaCatalog/repository.html*](http://martinfowler.com/eaaCatalog/repository.html)
137+
* **Edward Hieatt and Rob Mee. Repository pattern.**
138+
[_http://martinfowler.com/eaaCatalog/repository.html_](http://martinfowler.com/eaaCatalog/repository.html)
138139

139-
- **The Repository pattern**
140-
[*https://msdn.microsoft.com/library/ff649690.aspx*](https://msdn.microsoft.com/library/ff649690.aspx)
140+
* **The Repository pattern**
141+
[_https://msdn.microsoft.com/library/ff649690.aspx_](https://msdn.microsoft.com/library/ff649690.aspx)
141142

142-
- **Repository Pattern: A data persistence abstraction**
143-
[*http://deviq.com/repository-pattern/*](http://deviq.com/repository-pattern/)
143+
* **Repository Pattern: A data persistence abstraction**
144+
[_http://deviq.com/repository-pattern/_](http://deviq.com/repository-pattern/)
144145

145-
- **Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software.** (Book; includes a discussion of the Repository pattern)
146-
[*https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/*](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/)
146+
* **Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software.** (Book; includes a discussion of the Repository pattern)
147+
[_https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/_](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/)
147148

148149
### Unit of Work pattern
149150

150-
- **Martin Fowler. Unit of Work pattern.**
151-
[*http://martinfowler.com/eaaCatalog/unitOfWork.html*](http://martinfowler.com/eaaCatalog/unitOfWork.html)
151+
* **Martin Fowler. Unit of Work pattern.**
152+
[_http://martinfowler.com/eaaCatalog/unitOfWork.html_](http://martinfowler.com/eaaCatalog/unitOfWork.html)
152153

153154
<!-- -->
154155

155-
- **Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application**
156-
[*https://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application*](https://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application)
156+
* **Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application**
157+
[_https://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application_](https://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application)
157158

158159
### The Specification pattern
159160

160-
- **The Specification pattern.**
161-
[*http://deviq.com/specification-pattern/*](http://deviq.com/specification-pattern/)
161+
* **The Specification pattern.**
162+
[_http://deviq.com/specification-pattern/_](http://deviq.com/specification-pattern/)
162163

163-
- **Evans, Eric (2004). Domain Driven Design. Addison-Wesley. p. 224.**
164+
* **Evans, Eric (2004). Domain Driven Design. Addison-Wesley. p. 224.**
164165

165-
- **Specifications. Martin Fowler**
166-
[*https://www.martinfowler.com/apsupp/spec.pdf/*](https://www.martinfowler.com/apsupp/spec.pdf)
166+
* **Specifications. Martin Fowler**
167+
[_https://www.martinfowler.com/apsupp/spec.pdf/_](https://www.martinfowler.com/apsupp/spec.pdf)
167168

168-
>[!div class="step-by-step"]
169-
[Previous] (domain-events-design-implementation.md)
170-
[Next] (infrastructure-persistence-layer-implemenation-entity-framework-core.md)
169+
> [!div class="step-by-step"][previous] (domain-events-design-implementation.md)
170+
> [Next](infrastructure-persistence-layer-implemenation-entity-framework-core.md)

docs/standard/modern-web-apps-azure-architecture/test-asp-net-core-mvc-apps.md

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ ms.workload:
1010
- "dotnet"
1111
- "dotnetcore"
1212
---
13+
1314
# Test ASP.NET Core MVC Apps
1415

15-
> _"If you don't like unit testing your product, most likely your customers won't like to test it, either."_
16-
> _- Anonymous-
16+
> *"If you don't like unit testing your product, most likely your customers won't like to test it, either."*
17+
> \_- Anonymous-
1718
1819
## Summary
1920

@@ -100,19 +101,19 @@ Figure 9-3 Add an xUnit Test Project in Visual Studio
100101

101102
You should name your tests in a consistent fashion, with names that indicate what each test does. One approach I've had great success with is to name test classes according to the class and method they are testing. This results in many small test classes, but it makes it extremely clear what each test is responsible for. With the test class name set up to identify the class and method to be tested, the test method name can be used to specify the behavior being tested. This should include the expected behavior and any inputs or assumptions that should yield this behavior. Some example test names:
102103

103-
- CatalogControllerGetImage.CallsImageServiceWithId
104+
* CatalogControllerGetImage.CallsImageServiceWithId
104105

105-
- CatalogControllerGetImage.LogsWarningGivenImageMissingException
106+
* CatalogControllerGetImage.LogsWarningGivenImageMissingException
106107

107-
- CatalogControllerGetImage.ReturnsFileResultWithBytesGivenSuccess
108+
* CatalogControllerGetImage.ReturnsFileResultWithBytesGivenSuccess
108109

109-
- CatalogControllerGetImage.ReturnsNotFoundResultGivenImageMissingException
110+
* CatalogControllerGetImage.ReturnsNotFoundResultGivenImageMissingException
110111

111112
A variation of this approach ends each test class name with "Should" and modifies the tense slightly:
112113

113-
- CatalogControllerGetImage**Should**.**Call**ImageServiceWithId
114+
* CatalogControllerGetImage**Should**.**Call**ImageServiceWithId
114115

115-
- CatalogControllerGetImage**Should**.**Log**WarningGivenImageMissingException
116+
* CatalogControllerGetImage**Should**.**Log**WarningGivenImageMissingException
116117

117118
Some teams find the second naming approach clearer, though slightly more verbose. In any case, try to use a naming convention that provides insight into test behavior, so that when one or more tests fail, it's obvious from their names what cases have failed. Avoid naming you tests vaguely, such as ControllerTests.Test1, as these offer no value when you see them in test results.
118119

@@ -167,19 +168,7 @@ The \_logger and \_imageService are both injected as dependencies. Now you can t
167168

168169
## Integration Testing ASP.NET Core Apps
169170

170-
```cs
171-
}
172-
catch (FileNotFoundException ex)
173-
{
174-
throw new CatalogImageMissingException(ex);
175-
}
176-
}
177-
}
178-
```
179-
180-
This service uses the IHostingEnvironment, just as the CatalogController code did before it was refactored into a separate service. Since this was the only code in the controller that used IHostingEnvironment, that dependency was removed from CatalogController's constructor.
181-
182-
To test that this service works correctly, you need to create a known test image file and verify that the service returns it given a specific input. You should take care not to use mock objects on the behavior you actually want to test (in this case, reading from the file system). However, mock objects may still be useful to set up integration tests. In this case, you can mock IHostingEnvironment so that its ContentRootPath points to the folder you're going to use for your test image. The complete working integration test class is shown here:
171+
To test that a LocalFileImageService works correctly using an integraiton test, you need to create a known test image file and verify that the service returns it given a specific input. You should take care not to use mock objects on the behavior you actually want to test (in this case, reading from the file system). However, mock objects may still be useful to set up integration tests. In this case, you can mock IHostingEnvironment so that its ContentRootPath points to the folder you're going to use for your test image. The complete working integration test class is shown here:
183172

184173
```cs
185174
public class LocalFileImageServiceGetImageBytesById
@@ -188,6 +177,7 @@ public class LocalFileImageServiceGetImageBytesById
188177
private readonly Mock<IHostingEnvironment> _mockEnvironment = new Mock<IHostingEnvironment>();
189178
private int _testImageId = 123;
190179
private string _testFileName = "123.png";
180+
191181
public LocalFileImageServiceGetImageBytesById()
192182
{
193183
// create folder if necessary
@@ -196,33 +186,36 @@ public class LocalFileImageServiceGetImageBytesById
196186
System.IO.File.WriteAllBytes(filePath, _testBytes);
197187
_mockEnvironment.SetupGet<string>(m => m.ContentRootPath).Returns(GetFileDirectory());
198188
}
189+
199190
private string GetFilePath(string fileName)
200191
{
201192
return Path.Combine(GetFileDirectory(), "Pics", fileName);
202193
}
203194
private string GetFileDirectory()
204195
{
205-
var location = System.Reflection.Assembly.GetEntryAssembly().Location;
206-
return Path.GetDirectoryName(location);
207-
}
196+
var location = System.Reflection.Assembly.GetEntryAssembly().Location;
197+
return Path.GetDirectoryName(location);
198+
}
208199

209-
[Fact]
210-
public void ReturnsFileContentResultGivenValidId()
211-
{
212-
var fileService = new LocalFileImageService(_mockEnvironment.Object);
213-
var result = fileService.GetImageBytesById(_testImageId);
214-
Assert.Equal(_testBytes, result);
215-
}
200+
[Fact]
201+
public void ReturnsFileContentResultGivenValidId()
202+
{
203+
var fileService = new LocalFileImageService(_mockEnvironment.Object);
204+
var result = fileService.GetImageBytesById(_testImageId);
205+
Assert.Equal(_testBytes, result);
216206
}
207+
}
217208
```
218209

219210
> [!NOTE]
220-
> that the test itself is very simplethe bulk of the code is necessary to configure the system and create the testing infrastructure (in this case, an actual file to be read from disk). This is typical for integration tests, which often require more complex setup work than unit tests.
211+
> The test itself is very simplethe bulk of the code is necessary to configure the system and create the testing infrastructure (in this case, an actual file to be read from disk). This is typical for integration tests, which often require more complex setup work than unit tests.
221212

222213
## Functional Testing ASP.NET Core Apps
223214

224215
For ASP.NET Core applications, the TestServer class makes functional tests fairly easy to write. You configure a TestServer using a WebHostBuilder, just as you normally do for your application. This WebHostBuilder should be configured just like your application's real host, but you can modify any aspects of it that make testing easier. Most of the time, you'll reuse the same TestServer for many test cases, so you can encapsulate it in a reusable method (perhaps in a base class):
225216

217+
WIP: Update for 2.1
218+
226219
```cs
227220
public abstract class BaseWebTest
228221
{
@@ -233,7 +226,7 @@ public abstract class BaseWebTest
233226
{
234227
_client = GetClient();
235228
}
236-
229+
237230
protected HttpClient GetClient()
238231
{
239232
var startupAssembly = typeof(Startup).GetTypeInfo().Assembly;
@@ -274,6 +267,5 @@ public class CatalogControllerGetImage : BaseWebTest
274267

275268
This functional test exercises the full ASP.NET Core MVC application stack, including all middleware, filters, binders, etc. that may be in place. It verifies that a given route ("/catalog/pic/1") returns the expected byte array for a file in a known location. It does so without setting up a real web server, and so avoids much of the brittleness that using a real web server for testing can experience (for example, problems with firewall settings). Functional tests that run against TestServer are usually slower than integration and unit tests, but are much faster than tests that would run over the network to a test web server.
276269

277-
>[!div class="step-by-step"]
278-
[Previous] (work-with-data-in-asp-net-core-apps.md)
279-
[Next] (development-process-for-azure.md)
270+
> [!div class="step-by-step"][previous] (work-with-data-in-asp-net-core-apps.md)
271+
> [Next](development-process-for-azure.md)

0 commit comments

Comments
 (0)