๐ก Testing Logic Blocks
LogicBlocks are mockable and testable.
tip
You can see plenty of test examples for the tutorial project in the Chickensoft.LogicBlocks.Tutorial
package at the LogicBlocks repository.
๐ฅธ Mocking a Logic Blockโ
LogicBlocks implement the ILogicBlock<TState>
interface, which makes them easy to mock.
To make a logic block easily mockable, simply make it implement an interface.
public interface ITimer : ILogicBlock<Timer.State>;
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial class Timer : LogicBlock<Timer.State>, ITimer {
public override Transition GetInitialState() => To<State.PoweredOff>();
...
}
Then, we can mock whatever logic block api's we need to when testing an object which uses a logic block.
using static Chickensoft.LogicBlocks.Tutorial.Timer;
sealed record MyComponent(ITimer Timer) {
public void DoSomething() {
if (Timer.Value is State.PoweredOff) {
// Do something when the timer is off.
}
}
}
public class MyComponentTest {
[Fact]
public void Mocks() {
var timer = new Mock<ITimer>();
// Make the mock logic block be in a specific state.
timer.Setup(t => t.Value).Returns(new State.PoweredOff());
var component = new MyComponent(timer.Object);
component.DoSomething();
}
}
๐ฌ Testing a Logic Blockโ
Logic blocks themselves don't usually have much to test, but you can still do it for the sake of completeness.
[Fact]
public void Initializes() {
// Mock dependencies that the logic block needs.
var clock = new Mock<IClock>();
// Create the real logic block.
var timer = new Timer();
// Add the mocked dependencies to the blackboard.
timer.Set(clock.Object);
// Check that the initial state is the one we expect.
var state = timer.GetInitialState()
.State
.ShouldBeOfType<State.PoweredOff>();
// Verify the timer has set its blackboard data correctly.
timer.Has<Data>().ShouldBeTrue();
timer.Get<IClock>().ShouldBe(clock.Object);
}