I have been a developer for many years and get core development concepts, as well as unit testing ones such as DI/Ioc, Mocking etc. I also understand the values, importance of unit testing and writing good tests, as well as asking myself often: "What Would Roy Do?".
I am however slightly unsure on some approaches and principles to follow, and am not 100% sure of what I should be looking for; I am using NSubstitute
and NUnit
.
I am also using the EntityFramework
with a class mapping over the entities to provide extra functionality and strongly-type it to other domain models; these properties have been excluded for brevity.
Is it considered correct to create an interface
for my domain object
? If I want to write unit tests against services which return a domain object, or check the values of these objects (when they return a list of domain objects) I think it seems right, but this article seems to suggest I shouldn't.
Questions
How should the
value generation
be done? I'm not sure how we should make it react to different input parameters. I've implemented a way it could be done, but part of me feels this isn't right. What happens when I have more people added to it as I went to test, for example? Should I expand this section?Where should the
value generation
be done? Leaving it in the function feels wrong, but is clear as to how the values are generated/returned. Moving it to a function would make the test cleaner, and therefore easier to read (?), and usable in other tests? Or should I inject it (maybe withNInject
?) but this would require the creation of another service?
Interface
Public Interface IProductService
Function GetProductItems(userAs String) As List(Of IProductItem)
End Interface
Service Code
Namespace Services
Public Class ProductService
Implements IProductService
Public Function GetProductItems(userAs String) As List(Of IProductItem) Implements IProductService.GetProductItems
Dim result = New List(Of IProductItem)
Using con As New ProductEntities(Settings.Settings.ConnectionString)
For Each item In con.Product_Item.Where(Function(a) a.User.Equals(domainAndRacf, StringComparison.OrdinalIgnoreCase))
result.Add(New ProductItem(item))
Next
End Using
Return result
End Function
End Class
Domain Model Interface
Public Interface IProductItem
ReadOnly Property Id As Integer
End Interface
Domain Model
Namespace Classes
Public Class ProductItem
Implements IProductItem
Private _entity As Product_Item
Public ReadOnly Property Id As Integer Implements IProductItem.Id
Get
Return _entity.Id
End Get
End Property
## Constructors and other properties ommitted ##
End Class
End Namespace
Test Sample
Imports NSubstitute
Imports NUnit.Framework
Namespace Services
<TestFixture()>
Public Class ProductServiceTests
<TestCase("userA")>
<TestCase("userB")>
Public Sub GetProductItems_WithValidUser_ItemsForThatPerson(user As String)
' Arrange
Dim ProductService = New ProductService()
''''''''''''''''''''''''''''''''''
''''''vvv Value Generation vvv''''
Dim prod = Substitute.For(Of IProductItem)
Dim subProductService = Substitute.For(Of IProductService)()
subProductService.GetProductItems("userA").Returns(
New List(Of IProductItem) From {prod}
)
subProductService.GetProductItems("userB").Returns(
New List(Of IProductItem)
)
''''''^^^ Value Generation ^^^''''
''''''''''''''''''''''''''''''''''
' Act
Dim prodItems = ProductService.GetProductItems(user)
' Assert
CollectionAssert.AreEqual(prodItems, subProductService.GetProductItems(user))
End Sub
End Class
End Namespace
Extension
An extension to the above (which I hope is still classed as related, due to following a similar theme of principles and approaches), is about testing against a database accessed through the EntityFramework
DatabaseFirst.
There are other posts talking about mocking the entire framework, but this to me feels overkill. Is there a better way than creating hand-written substitutions for every single table and value within there?
I'm wanting to make the tests as lean and independent as I can, so when new developers join the team it is easy and straightforward for them to pick up.