Testing Architecture
Introduction
Testing will be used to ensure the quality of the application while also helping us reach faster releases thanks to automated regression testing. These tests will be used mostly on Javascript components (frontend), C# components (backend) as well as any React component that does business or logical work with API data. Tests can also be used to test that we are fetching data from an API correctly if needed.
The Scope of our testing remains on unit tests and regression tests that could be made automatic. This will help give us fast feedback and stable release testing whenever a new component is introduced. Regression tests will be necessary as well to ensure the stability of older components.
Unit Tests
Unit tests are for testing individual functions, classes or methods that will be doing some form of business or logical work. For example if a class takes a data parameter, does some work and then adds something into the SQL database the data parameters need to be tested with intended and unintended data to ensure error handling works correctly. General guidelines for quick unit tests are to do 3 tests using specific data, these are.
- Use the smallest intended data possible
- Use the largest intended data possible
- Use unintended data to ensure it gets caught by error handling methods
(Our unit tests that don't use numerical data will in some cases only need to do test 3 and test 1 or 2 not all three.)
Rules for unit tests
For these tests we want to avoid using our real database as a testing ground and instead use a mock database instead. Any tests done on the actual database would need to have test data removed later which can become more complicated and bloats the database
Unit tests will be used to test data insertion and logical tasks being run by backend and frontend components for.
- C# classes and methods
- JavaScript logical functions and methods
The Golden Rule of Assertions
- Golden rule of Assertions
- A unit test must fail if, and only if, the intention behind the system is not met.
- A test should verify one clear behavior,
- A failing test should immediately tell what behavior is broken,
- A test should not fail because of unrelated changes in the code,
- Assertions should reflect the intention of the test, not the implementation details.
Naming
- The name of the unit test should describe the intent of the test clearly.
- The name should consist of three parts.
- Name of the method being tested,
- The scenario of testing
- The expected behavior
MethodName_StateUnderTest_ExpectedBehavior.
- The test should be written in an AAA pattern, which is a common pattern for unit tests. It provides clear separation in the test.
- The A stands for Arrange, Act, and Assert.
- Arrange - we create essential variables and objects for the test.
- Act - we call the method being tested and receive the value.
- Assert - we check expected and actual value. This determines if the test passed or not. If the expected value and the actual value are equal, the test passed the unit test.
public void IsPrime_WhenNumberIsPrime_ReturnsTrue()
{
// Arrange
var primeUtils = new PrimeUtils();
int number = 5;
bool expected = true;
// Act
var actual = primeUtils.IsPrime(number);
// Assert
Assert.Equal(expected, actual);
}
Avoid
- Avoid using complex logic such as if, for, while and switch.
- Avoid having multiple acts. If we have multiple acts and asserts, it is not clear where the method failed. Instead, use two unit tests to test two different values.
Other
-
We can make helper methods to set up the unit tests if we want to test multiple values. This test receives the numbers you want to test and can therefore be used for different values.
-
Create mockup data where it is needed to do a unit test.
-
Unit test methods should be independent of other unit test methods.
- Example:
[Theory]
[InlineData(2, 3, 5)]
[InlineData(11, 5, 16)]
public void Add_TwoNumbers_ReturnsSum(int number1, int number2, int expected)
{
// Arrange
var calculator = CreateCalculator();
// Act
var actual = calculator.Add(number1, number2);
// Assert
Assert.Equal(expected, actual);
}
private Calculator CreateCalculator()
{
return new Calculator();
}
Where to write unit tests?
- Unit tests should be written in a seperate file, not in the file with the source code.
- For example, a testfile for the middleware should be located at middleware/Program.test.cs. While the code is in middleware/Program.cs.
- The test file should have the same name as the file containing the code being tested, but with ".test" after the name of the file.
Regression and Automated testing
Regression tests ensure that previously working components don't break after new components get introduced into the application. This will mostly be done by redoing existing component unit tests and, primarily, by writing Javascript unit tests in Jest and C# unit tests in xUnit.
By using Jest and xUnit we can make all our tests automated for Javascript and C# and they can be executed locally in the folder where the test exists
This testing strategy allows for faster feedback and faster execution enabled by Jest and Xunit which allows us to run regression tests frequently
Regression test coverage is built incrementaly over time by handling bug fixes and logic testing in components. Essentially they are our existing unit tests that will be run again as a safety net for our older components and by implementing unit tests using Jest or Xunit they can easier be made automated to run continuously or before any important merges.
1. Setup
2. Standards
- Coding Conventions
- Issues
- Branch creation
- Reviews
- Implementation Standards
- [WIP] Creating new databases
- Localization
3. Models and Diagrams
4. Testing
5. Documentation
- Documentation for service endpoints
- API documentation
- Webpage Design
- Secrets and .env
- Evaluations
- Installation and Rebuild script documentation
6. Guides and How-to's
7. Micro Service Mockup Api
- Guidelines Mircro Service Mockup
- Documentation of useTemperature/useTemperatureTimeSeries mockup sensor