This is a detailed topic in our support portal in the Using Hopp series and assumes that you have some prior knowledge or experience using Hopp. |
From version 1.11. Hopp makes the generated engine code unit test friendly. It is now pretty straight forward to write unit tests. If you are implementing Manual Rules and Bags in Visual Studio, this article is for you.
You can use any test framework and any mocking library to create unit tests. In the samples below, we have used
- XUnit for unit test
- FakeItEasy for mocking
- Fluent Assertions for easy-to-read test assertion
In order to be able to unit test Bags and Rules, you need to mock the appropriate context interface. There are 3 different interfaces to consider:
Source Engine:
- IItemExporterContext
Target Engine
- IItemImportContext
- IItemImportContextMapping (contains a couple of methods only available to Mapping Rules)
These interfaces contain the properties and methods available to Bags and Rules. In order to unit test a given Bag/Rule, you only need to mock the members on the interface that is actually used.
As an example, let's look at the Target Engine Mapping Rule GetFrequencyId from the Hopp Online training exercises:
The rule takes a FlagHandler delegate to use to raise flags and a frequencyShortName:
- If the frequencyShortName is null or empty, the rule raises flag 2 and return null
- If not, the rule looks up the frequencyShortName in the Valuset Frequencies
- If a row is not found, it raises flag 1 and returns null
- If a row is found it raises no flags and returns the FrequencyId on the row.
In the following, we will look at 3 unit tests for the GetFrequencyId mapping rule:
- ShouldRaiseFlag2IfFrequencyShortNameNotProvided
- ShouldRaiseFlag1IfFrequencyNotFound
- ShouldReturnFoundValueIfSuccessful
A couple of things to note all manual rules and bags
- In the Target Engine, Manual Rules and Bags exist in the namespace MigFx.Engine.Project.Target
- In the Source Engine, Manual Rules and Bags exist in the namespace MigFx.Source.Project
- Manual Rules are in fact virtual methods in an abstract, generated base class that are overridden in a generated, derived class marked as partial
- Each rule is implemented in a separate file that is a partial implementation of this derived class
- For example, the GetFrequencyId rule above is a method inside the partial class MappingRules
In order to test the GetFrequencyId rule, you need to instantiate an instance of the MappingRules class and call the GetFrequencyId method. Since this is a Target Engine Mapping Rule, the constructor for the MappingRules class takes a single parameter of type IItemImportContextMapping.
ShouldRaiseFlag2IfFrequencyShortNameNotProvided
This is the simplest test, since this execution path in the rule does not access any members on the IItemImportContextMapping so a minimal of mocking is required.
// Arrange
- Using FakeItEasy, create a mock (a 'fake') of the IItemImportContextMapping
- Get a sut (System under Test) by constructing an instance of the MappingRules class, passing the fake context.
- Create a list of int to keep track of the flags raised by the rule
- Create a local method with the same signature as the FlagHandler delegate to be passed to the rule. All flags raised by the rule will be stored in the list of int
// Act
- Test the rule by calling the rule method on the sut with the local flagHandler and a null value for the frequencyShortName
- Store the return value in the result variable
// Assert
- Using Fluent Assertions, assert that
- The rule returns a null value
- Exactly one flag is raised
- The flag number 2 is raised
ShouldRaiseFlag1IfFrequencyNotFound
For this test, the rule will attempt to lookup a row in the Frequencies Valueset. So, this Valueset must be mocked. Since the test is that rule should not find anything, it is sufficient to mock an empty Valueset.
// Arrange
- Using FakeItEasy
- Create a fake IValuesets instance. The IValuesets interface is generated and contains a get property for each Valueset
- Mock a call to the Frequencies property on the fake IValuesets to return an empty collection of valueset rows. The Valueset.IFrequencies interface is generated and contains get properties for the columns of the valueset
- Create a fake IItemImportContextMapping instance
- Mock a call to the Valueset property on the fake context to return the fake IValuesets created earlier
- Get a sut (System under Test) by constructing an instance of the MappingRules class, passing the fake context.
- Create a list of int to keep track of the flags raised by the rule
- Create a local method with the same signature as the FlagHandler delegate to be passed to the rule. All flags raised by the rule will be stored in the list of int
// Act
- Test the rule by calling the rule method on the sut with the local flagHandler and a dummy value for the frequencyShortName
- Store the return value in the result variable
// Assert
- Using Fluent Assertions, assert that
- The rule returns a null value
- Exactly one flag is raised
- The flag number 1 is raised
ShouldReturnFoundValueIfSuccessful
For this test, the rule will again attempt to lookup a row in the Frequencies Valueset and this Valueset must be mocked. Since the test is that the rule should now successfully find a row, the mocked Valueset must contain a row to find - plus a couple of other rows to ensure it finds the correct one.
// Arrange
- Set up a couple of variables to contain the frequencyShortName to lookup and the expected frequencyId to be found
- Using FakeItEasy
- Create a fake IValuesets instance. The IValuesets interface is generated and contains a get property for each Valueset
- Create collection of valueset rows. One of the rows has the correct value for frequencyShortName and frequencyId. The Valueset.FrequenciesRow class is generated and contains getter and setter properties for the Valueset columns
- Mock a call to the Frequencies property on the fake IValuesets to return collection of valueset rows created above
- Create a fake IItemImportContextMapping instance
- Mock a call to the Valueset property on the fake context to return the fake IValuesets created earlier
- Get a sut (System under Test) by constructing an instance of the MappingRules class, passing the fake context.
- Create a list of int to keep track of the flags raised by the rule
- Create a local method with the same signature as the FlagHandler delegate to be passed to the rule. All flags raised by the rule will be stored in the list of int
// Act
- Test the rule by calling the rule method on the sut with the local flagHandler and the correct frequencyShortName stored above
- Store the return value in the result variable
// Assert
- Using Fluent Assertions, assert that
- The rule does not raise any flags
- The rule returns the expected frequencyId from the correct valueset row
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article