Recently, I am learning unit test and try to use this skill in my project.
However, I have some question about how to test following code.
public class Class1
{
private Timer _timer = new Timer();
public Class1()
{
_timer.Interval = 1000;
_timer.Elapsed += _timer_Elapsed;
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
//some works
}
public void start()
{
_timer.Start();
//some works
}
public void stop()
{
_timer.Stop();
//some works
}
}
I truly don't know how to test this class without breaking its encapsulation.
Should I expose '_timer_Elapsed' method? Evan if do so, next question comes to how to test method 'start' and 'stop'. Should I use stub object to substitute the Timer in order to test this class? if that, this class will be too complex because I need to make an interface and make a class which encapsulates Timer and implement the interface above.
public interface IEncal
{
double Interval { get; set; }
event ElapsedEventHandler Elapsed;
void Stop();
void Start();
}
public class MyClass : IEncal
{
Timer timer = new Timer();
public double Interval { get { return timer.Interval; } set { timer.Interval = value; } }
public event ElapsedEventHandler Elapsed
{
add { timer.Elapsed += value; }
remove { timer.Elapsed -= value; }
}
public void Start()
{
timer.Start();
}
public void Stop()
{
timer.Stop();
}
}
public class Class1
{
private IEncal _timer;
public Class1(IEncal timer)
{
_timer = timer;
_timer.Interval = 1000;
_timer.Elapsed += _timer_Elapsed;
}
public void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
//some works
}
public void start()
{
_timer.Start();
//some works
}
public void stop()
{
_timer.Stop();
//some works
}
}
Is there any better way to test this class?
The following is the real code I use.
I've written the unit test for ISender class and IInfoProducer class.
Since it's my first time to use unit test, I would like to think about each class for learning.
Back to the question, this class is basically a Timer class. When elapsing ,
it sends a info. So how should I test this timing sending process?
public class InfomationSender
{
private readonly Timer _timer = new Timer();
private readonly ISender _sender;
private readonly IInfoProducer _producer;
public InfomationSender(ISender sender, IInfoProducer producer)
{
_sender = sender;
_producer = producer;
_timer.Interval = 1000;
_timer.Elapsed += _timer_Elapsed;
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
var info = _producer.getInfo();
_sender.send(info);
}
public void start()
{
_timer.Start();
}
public void stop()
{
_timer.Stop();
_sender.Dispose();
}
}
You should be able test the class without mocking. Less mocking more valuable tests are.
But you didn't provide information about what your code is doing in // some works areas. Because this is what you want to test, timer is just implementation details.
For testing "some works" class should provide some public access to the result of "some works" or result will be "exposed" through injected dependency (repository, logger etc)
Assume class will execute "some work" every second and update Result property after Stop method is called.
public class LiveCalculator
{
private readonly Timer _timer;
private List<int> _allValues
private int _lastSum;
public int LastSum => _lastSum;
public LiveCalculator()
{
_allValues = new List<int>();
_timer = new Timer { Interval = 1000 };
_timer.Elapsed += _timer_Elapsed;
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
_allValues.Add(42);
}
public void start()
{
_timer.Start();
}
public void stop()
{
_timer.Stop();
_lastResult = _allValues.Sum();
}
}
Tests
[Fact]
public async Task AfterFiveSeconds_LastResultEquals210()
{
var calculator = new LiveCalculator();
calculator.Start();
await Task.Delay(5100);
calculator.Stop();
calculator.LastResult.Should().Be(210);
}
Related
Hello I have a Blazor server app where some PLC variables are read from a remote production machine.
The code part that is connecting to the PLC and reading the datas is in a sourced service (PLCService.cs). The service is injected and called in a razor page.
If I trigger the reading manualy with a button, all the variables are read correctly. So far no problem. But I want that the variables are read every second from the remote machine's PLC. Therefore I have programmed a timer in the codebehind page of my razor page (not in the service), but the timer is not working. (The values are note read even once)
In the razor page there are also the variables that I read from the PLC, but for making it leaner I have just shown the counter value, that should count each second.
In my razor file:
#inject PLCService PLCService
<button #onclick="Read_CNC_Status">Read PLC Data</button>
<l>#counter_timer</l> // Unfortunately the counter value is always "0"
In my razor.cs file:
using System.Timers;
public partial class Read_PLC_Data
{
public int counter_timer=0;
System.Timers.Timer timer1 = new System.Timers.Timer();
public async void Read_CNC_Status()
{
PLCService.Connect_PLC(); // Connection code is in a sourced service
Initialise_Timer1();
}
public void Initialise_Timer1()
{
timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent1);
timer1.Interval = 1000;
timer1.Enabled = true;
counter_timer = 0;
}
public void OnTimedEvent1(object source, ElapsedEventArgs e)
{
PLCService.Read_PLC_Data();
counter_CNC_status += 1; // This counter is not counting !!!
if(counter_timer >= 30)
{
counter_timer = 0;
timer1.Enabled = false;
}
StateHasChanged();
}
}
Try the periodic timer:
private readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(5));
public async void OnGet()
{
while(await _periodicTimer.WaitForNextTickAsync())
{
await ConnectPlc();
}
}
UPDATE 1
If you want to go with your approach you should use InvokeAsync(StateHasChanged) because Blazor would not recognize the state change and not refresh the UI
<h3>#_currentCount</h3>
#code {
private int _currentCount;
private System.Timers.Timer _timer;
protected override void OnInitialized()
{
_timer = new();
_timer.Interval = 1000;
_timer.Elapsed += async (object? sender, ElapsedEventArgs e) =>
{
_currentCount++;
await InvokeAsync(StateHasChanged);
};
_timer.Enabled = true;
}
}
Don't forget to invoke the StateHasChanged method
UPDATE 2
If you want to use the periodic timer that i initially suggest, you can use it this way:
First let's assume that you have a class that is responsible for PLC data and implements the INotifyPropertyChanged interface:
public class PlcData : INotifyPropertyChanged
{
private readonly ILogger<PlcData> _logger;
public event PropertyChangedEventHandler? PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private int _counter;
public int Counter
{
get { return _counter; }
set
{
if (_counter != value)
{
_counter = value;
NotifyPropertyChanged(nameof(Counter));
}
}
}
public PlcData(ILogger<PlcData> logger)
{
_logger = logger;
}
public async Task GetFromPlcAsync()
{
_logger.LogInformation("Get new info: {c}", ++Counter);
await Task.CompletedTask;
}
}
Then Create a background service
public class PlcService : BackgroundService
{
private readonly PlcData _plc;
private readonly PeriodicTimer _timer;
public PlcService(PlcData plc)
{
_plc = plc;
_timer = new(TimeSpan.FromSeconds(1));
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while(await _timer.WaitForNextTickAsync(stoppingToken)
&& !stoppingToken.IsCancellationRequested)
{
await _plc.GetFromPlcAsync();
}
}
}
In razor page your need to inject the PlcData
#inject PlcData plcData
<h3>#_currentCount</h3>
#code {
private static int _currentCount;
protected override void OnInitialized()
{
plcData.PropertyChanged += OnIncrement;
}
private async void OnIncrement(object? sender, PropertyChangedEventArgs e)
{
_currentCount = plcData.Counter;
await InvokeAsync(() =>
{
StateHasChanged();
});
}
}
Also you need to add services to the container.
builder.Services.AddHostedService<PlcService>();
...
...
builder.Services.AddSingleton<PlcData>();
Here is a typical Program.cs from .net6 Blazor-Server And that's it!
UPDATE 3
Using System.Threading
#page "/"
#using System.Threading;
<h3>#_currentCount</h3>
#code {
private int _currentCount;
protected override void OnInitialized()
{
var timer = new Timer(new TimerCallback(_ =>
{
_currentCount++;
InvokeAsync(() =>
{
StateHasChanged();
});
}), null, 1000, 1000);
}
}
I don't see where you are calling the Read_CNC_Status() method. If you don't call it, then nothing you wrote about the timer is ever executed. You can call it in the component's OnAfterRender function (or in the constructor of your partial class).
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
Read_CNC_Status();
}
base.OnAfterRender(firstRender);
}
I ran the rest of the code and it works.
EDIT: It's not necessary, but I also recommend that you make sure the handler is not called again before the previous execution is finished (this could happen for example if Read_PLC_Data() is slow and takes more than the timer's Interval to complete). To do that, you can set the AutoReset property of your timer to false and manually restart the timer each time at the end of your handler, like this:
public async void Read_CNC_Status()
{
PLCService.Connect_PLC(); // Connection code is in a sourced service
Initialise_Timer1();
}
public void Initialise_Timer1()
{
timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent1);
timer1.Interval = 1000;
timer1.AutoReset = false;
counter_CNC_status = 0;
timer1.Start();
}
public void OnTimedEvent1(object source, ElapsedEventArgs e)
{
try
{
PLCService.Read_PLC_Data();
counter_CNC_status += 1; // This counter is not counting !!!
}
finally
{
if(counter_CNC_status >= 30)
{
counter_CNC_status = 0;
timer1.Enabled = false;
}
else
{
timer1.Start();
}
}
StateHasChanged();
}
The best approach for me is to use QUARTZ.NET
I have a developing a c# windows form application and I have a method that exists inside the main form class.
Imagine methodA as part of the main form class.
public void methodA() {
A.someMethod();
B.someMethod();
// some more code
if (someCondition) {
// execute some code
}
// initialize timer and set event handler for timer
// run new thread
}
class A {
someMethod() {...}
}
class B {
someMethod() {...}
}
How would I run tests to test the branch logic of this methodA (isCondition)? since it involves initializing timer and running threads. Can i only verify the logic while doing system test ? I dont think it is possible to mock the timer and threading function.
Thank you !
Of course you can mock the timer. This is by creating a new interface, say, ITimerWrapper and implement it by using the concrete Timer class. Basically a wrapper of the Timer class. Then use that instead of the concrete Timer class you have.
Something in the tune of:
public partial class Form1 : Form
{
private readonly ITimerWrapper _timerWrapper;
public Form1(ITimerWrapper timerWrapper)
{
InitializeComponent();
this._timerWrapper = timerWrapper; // of course this is done via dependency injection
this._timerWrapper.Interval = 1000;
}
private void Form1_Load(object sender, EventArgs e)
{
// now you can mock this interface
this._timerWrapper.AddTickHandler(this.Tick_Event);
this._timerWrapper.Start();
}
private void Tick_Event(object sender, EventArgs e)
{
Console.WriteLine("tick tock");
}
}
public interface ITimerWrapper
{
void AddTickHandler(EventHandler eventHandler);
void Start();
void Stop();
int Interval { get; set; }
}
public class TimerWrapper : ITimerWrapper
{
private readonly Timer _timer;
public TimerWrapper()
{
this._timer = new Timer();
}
public int Interval
{
get
{
return this._timer.Interval;
}
set
{
this._timer.Interval = value;
}
}
public void AddTickHandler(EventHandler eventHandler)
{
this._timer.Tick += eventHandler;
}
public void Start()
{
this._timer.Start();
}
public void Stop()
{
this._timer.Stop();
}
}
Then for the spinning of a new thread, that's also testable by doing the same thing.
Bottomline is to have an interface to separate concerns and mock the interface on your unit test.
I have this class
public class ResetClock
{
public delegate void ResetFunc();
private ResetFunc m_ResetFunc;
private event ResetFunc Reseted;
private Timer m_Timer;
public ResetClock(int second)
{
double r = second * 1000;
m_Timer = new Timer(r);
m_Timer.AutoReset = false;
m_Timer.Elapsed += new ElapsedEventHandler(onTimerTick);
Reset();
}
public void SetResetMethod(ResetFunc method)
{
m_ResetFunc = method;
}
public void Terminate()
{
m_Timer.Stop();
m_Timer.Dispose();
m_Timer = null;
}
private void onTimerTick(object sender, ElapsedEventArgs e)
{
if (m_ResetFunc == null)
{
if (Reseted != null) Reseted();
}
else
{
m_ResetFunc();
}
Reset();
}
#region Public
public void Reset()
{
m_Timer.Reset();
}
#endregion
}
internal static class TimerEx{
public static void Reset(this Timer timer){
timer.Stop();
timer.Start();
}
}
My application is showing the reset page when this class reaches the seconds defined in the contractor. when a Reset() method calls, the Timer should reset the it self and begin the timer from 0.
for some reason after the application is running few days the reset event is firing after few seconds and not the number of seconds that i defined in the first application initialization
does anyone have any idea....??? I braking my head through the wall to find the answer..
I have implemented a SqlListener class that uses SqlDependency to wait for changes in SQL database. At one point in my business workflow I need to wait for a record turning up in the database. The SqlListener triggers an event when requested record is found. This works fine. I can make it work by entering a While-loop and wait until I detect the event being returned. But this is not ideal design. It makes the processor spin a lot in vain.
I would like to wait for the event in a more intelligent manner. I read a lot of suggestions on using Task, NotificationDelegate, ManualResetEvent, etc. .... but I was not able to get it all together.
A simplified example will probably make it easier to understand. This is my current setup that works. But if possible I would like to get rid of the ugly while loop.
private const int MaxWaitTime = 5;
private SqlListener<RecordType> _recordListener;
private RecordType _record;
/// <summary>
/// Request a record and wait until it is found.
/// </summary>
public RecordType GetRecordAwait(int requestedId)
{
// Initiate listening for record
_recordListener = new SqlListener<RecordType>();
_recordListener.SqlModified += SqlListener_SqlModified;
_recordListener.StartListening(requestedId);
// Wait until record is found
var startTime = DateTime.Now;
while (_record == null &&
DateTime.Now.Subtract(startTime).TotalSeconds < MaxWaitTime)
{
Thread.Sleep(1);
}
// Stop listening
_recordListener.SqlModified -= SqlListener_SqlModified;
_recordListener.Dispose();
_recordListener = null;
// Return record
return _record;
}
private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
{
_record = (RecordType)args.Record;
}
Instead of using While, you could go with Timer and events. Something like:
public class ListenerWaiting
{
public ListenerWaiting(int waitingTimeSeconds)
{
_waitSeconds = waitingTimeSeconds;
}
private int _waitSeconds;
private System.Timers.Timer _timer;
private Listener _listener;
public event EventHandler<string> ListenerDone;
public void Listen(int listeningPeriodSeconds)
{
_listener = new Listener(listeningPeriodSeconds * 1000);
_listener.ListenerCompleted += ListenerListenerCompleted;
_timer = new System.Timers.Timer(_waitSeconds * 1000) {Enabled = true};
_timer.Elapsed += TimerElapsed;
}
void ListenerListenerCompleted(object sender, string e)
{
StopTimer();
StopListener();
if (ListenerDone != null)
ListenerDone(this, "Waiting success! Message was: " + e);
}
void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
StopTimer();
StopListener();
if (ListenerDone != null)
ListenerDone(this, "Waited longer than set, aborted waiting...");
}
private void StopTimer()
{
_timer.Stop();
_timer.Elapsed -= TimerElapsed;
_timer = null;
}
private void StopListener()
{
_listener.ListenerCompleted -= ListenerListenerCompleted;
_listener = null;
}
}
public class Listener
{
private System.Timers.Timer _timer;
private string _listeningPeriodSeconds;
public event EventHandler<string> ListenerCompleted;
public Listener(int listeningPeriodSeconds)
{
_listeningPeriodSeconds = listeningPeriodSeconds.ToString();
_timer = new System.Timers.Timer(listeningPeriodSeconds) { Enabled = true };
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Elapsed -= TimerElapsed;
_timer = null;
if (ListenerCompleted != null)
ListenerCompleted(this, _listeningPeriodSeconds);
}
}
...and then consume it with:
static void Main(string[] args)
{
var wait = new ListenerWaiting(5);
wait.ListenerDone += WaitListenerDone;
wait.Listen(3);
Console.ReadLine();
}
static void WaitListenerDone(object sender, string e)
{
Console.WriteLine(e);
}
I guess I could find better names for classes, but you'll get the idea ;)
In fact the solution was more simple than I first thought. When I rephrased my question and searched again I found it. The ManualResetEvent as mentioned already in my question turned out to be the simplest way to solve it.
All I had to do was to add a ManualResetEvent and set it to wait ;-)
private const int MaxWaitTime = 5000;
private SqlListener<RecordType> _recordListener;
private RecordType _record;
private readonly ManualResetEvent _recordWaiter = new ManualResetEvent(false);
/// <summary>
/// Request a record and wait until it is found.
/// </summary>
public RecordType GetRecordAwait(int requestedId)
{
// Initiate listening for record
_recordListener = new SqlListener<RecordType>();
_recordListener.SqlModified += SqlListener_SqlModified;
_recordListener.StartListening(requestedId);
// Wait synchronously until record is found
_recordWaiter.WaitOne(MaxWaitTime);
// Stop listening
_recordListener.SqlModified -= SqlListener_SqlModified;
_recordListener.Dispose();
_recordListener = null;
// Return record
return _record;
}
private void SqlListener_SqlModified(object sender, SqlModifiedArgs args)
{
_record = (RecordType)args.Record;
_recordWaiter.Set();
}
I am trying to fire the event handler assigned to my timer mock. How can I test this private method here?
public interface ITimer
{
void Start();
double Interval { get; set; }
event ElapsedEventHandler Elapsed;
}
Client class assigns an event handler to this object. I want to test the logic in this class.
_timer.Elapsed += ResetExpiredCounters;
And the assigned method is private
private void ResetExpiredCounters(object sender, ElapsedEventArgs e)
{
// do something
}
I want to have this event handler in my mock and run it somehow. How can I do this?
Update:
I realized I was raising the event before I assigned the event handler. I corrected that but I still get this error:
System.ArgumentException : Object of type 'System.EventArgs' cannot be converted
to type 'System.Timers.ElapsedEventArgs'.
I raise it like this:
_timer.Raise(item => item.Elapsed += null, ElapsedEventArgs.Empty);
or
_timer.Raise(item => item.Elapsed += null, EventArgs.Empty);
Both won't work.
Update:
Here's the thing that worked for me. Note that it's not useful if you are trying to pass info to event handler like Jon pointed out in comments. I am just using it to mock the wrapper for System.Timers.Timer class.
_timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);
In the end, this won't help at all if you need to use event arguments since it will be always null. However, it's the only way since ElapsedEventArgs has only an internal constructor.
ElapsedEventArgs has a private constructor and can not be instantiated.
If you use:
timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);
Then the handler will recevie a null parameter and lose its SignalTime property:
private void WhenTimerElapsed(object sender, ElapsedEventArgs e)
{
// e is null.
}
You might want this parameter in some cases.
To solve this and make it more testable, I also created a wrapper for the ElapsedEventArgs, and made the interface use it:
public class TimeElapsedEventArgs : EventArgs
{
public DateTime SignalTime { get; private set; }
public TimeElapsedEventArgs() : this(DateTime.Now)
{
}
public TimeElapsedEventArgs(DateTime signalTime)
{
this.SignalTime = signalTime;
}
}
public interface IGenericTimer : IDisposable
{
double IntervalInMilliseconds { get; set; }
event EventHandler<TimerElapsedEventArgs> Elapsed;
void StartTimer();
void StopTimer();
}
The implementation will simply fire its own event getting the data from the real timer event:
public class TimerWrapper : IGenericTimer
{
private readonly System.Timers.Timer timer;
public event EventHandler<TimerElapsedEventArgs> Elapsed;
public TimeSpan Interval
{
get
{
return this.timer.Interval;
}
set
{
this.timer.Interval = value;
}
}
public TimerWrapper (TimeSpan interval)
{
this.timer = new System.Timers.Timer(interval.TotalMilliseconds) { Enabled = false };
this.timer.Elapsed += this.WhenTimerElapsed;
}
private void WhenTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
var handler = this.Elapsed;
if (handler != null)
{
handler(this, new TimeElapsedEventArgs(elapsedEventArgs.SignalTime));
}
}
public void StartTimer()
{
this.timer.Start();
}
public void StopTimer()
{
this.timer.Stop();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.timer.Elapsed -= this.WhenTimerElapsed;
this.timer.Dispose();
}
this.disposed = true;
}
}
}
Now, you can simplify and improve the mock of this event:
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs());
var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs(yesterday));
Less code to write, easier to work with and completely decoupled from the framework.
The Moq QuickStart guide has a section on events. I think you'd use
mock.Raise(m => m.Elapsed += null, new ElapsedEventArgs(...));
Dealt with this recently, you can construct an ElapsedEventArgs using reflection:
public ElapsedEventArgs CreateElapsedEventArgs(DateTime signalTime)
{
var e = FormatterServices.GetUninitializedObject(typeof(ElapsedEventArgs)) as ElapsedEventArgs;
if (e != null)
{
var fieldInfo = e.GetType().GetField("signalTime", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
fieldInfo.SetValue(e, signalTime);
}
}
return e;
}
This way you can continue using the original ElapsedEventHandler delegate
var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, CreateElapsedEventArgs(yesterday));
Could do something like this to wrap your Timer
public class FakeTimer : IMyTimer
{
private event ElapsedEventHandler elaspedHandler;
private bool _enabled;
public void Dispose() => throw new NotImplementedException();
public FakeTimer(ElapsedEventHandler elapsedHandlerWhenTimeFinished, bool startImmediately)
{
this.elaspedHandler = elapsedHandlerWhenTimeFinished;
_enabled = startImmediately;
}
public void Start() => _enabled = true;
public void Stop() => _enabled = false;
public void Reset() => _enabled = true;
internal void TimeElapsed()
{
if (this._enabled)
elaspedHandler.Invoke(this, new EventArgs() as ElapsedEventArgs);
}
}