Turn your tests into stress tests easily
It’s great to be able to write tests and execute them while developing a project. While I’m developing, I can hit a button and run the dozens of tests to see if I’ve broken anything.
As code gets written lots of things get refactored, moved around, etc. Hitting a button to see if I broke anything is hugely useful.
Tests are also a great defense against future changes, by me or others
However, the VS test framework doesn’t allow me directly to repeat tests, making it hard to look for leaks.
It’s actually not too hard to make a test repeat for an arbitrary number of times. And….I’m not saying to modify the test and surround it with a FOR loop.
In this post, Create your own Test Host using XAML to run your unit tests, I showed how you can make your own test host to run your tests.
However there’s a way to do iterate your tests that’s easier. This is especially beneficial if you have thousands of existing “functional” tests exercising your code, but you want to see if any have leaks, without modifying the code much.
Below is a way to do so, without changing the core test code at all. It can be done by making the test derive from a base class with the functionality, or just changing the TestCleanup method.
The key is to get a reference to the test method itself, then invoke it from the test.
The iteration functionality can be driven by parameters, perhaps in a XML config file somewhere.
Further enhancements:
· Wait to see if the test crashes due to out of memory exceptions
· Watch Task Manager to see if memory use grows
· Observe Performance counters while iterating the tests via Performance Monitor
· Modify the code to read Performance Counters explicitly
Start Visual Studio 2012 (or 2010)
Create a new C# WPF Application. Mine is called “WpfApplication1”
File->Add->New Project->C#->Unit Test Project. Mine is called “UnitTestProject1”
Add a reference from the Test project to the WPF project:
Project->Add reference-> Projects->WpfApplication1
Add a reference from the Test project to Framework Assemblies so the Test code can execute WPF
PresentationFramework, PresentationCore, System.Xaml, WindowsBase
Open the Test Explorer Window.
Test->Windows->Test Explorer
Add 2 lines of code to TestMethod1:
var x = new WpfApplication1.MainWindow();
x.ShowDialog();
Hit Ctrl-R + T (that’s 2 separate keystrokes : Control-R, then T), which will run the test and show the Main Window. Close the window and the test will finish.
Change the “ShowDialog” call to “Show” so it will show and close automatically.
Paste in the code below, then hit Ctrl-R+L (to repeat the last test run).
Make sure these two options are checked:
Tools->options->Environment->Documents->Detect when file is changed outside the environment
Tools->options->Environment->Documents->Detect when file is changed outside the environment->Auto-load changes, if saved.
Open the log file in Visual Studio (default file name: “c:\t.txt”). Dock it to the right of the main code window, so you can see both the code and the log.
As the test runs, the log gets updated and VS will show the updates.
See also:
The number of Garbage Collections indicate how much memory is used
Examine your program's available memory: is it leaking?
Automatic tests protect your code
<code>
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
string LogFileName = @"c:\log.txt"; // some accessible easy to type path
// the test execution engine reflects, looking for "TestContext" and sets it per test
public TestContext TestContext { get; set; }
[TestInitialize]
public void TestInit()
{
if (File.Exists(LogFileName))
{
File.Delete(LogFileName);
}
}
public void LogString(string strMsg, params object[] args)
{
try
{
strMsg = string.Format(strMsg, args);
var str = string.Format(@"[{0:hh\:mm\:ss\.fff t}], {1}" + "\r\n", DateTime.Now, strMsg);
File.AppendAllText(LogFileName, str);
}
catch (IOException)
{ //System.IO.IOException: The process cannot access the file
// 'C:\log.txt' because it is being used by another process.
System.Threading.Thread.Sleep(100);
LogString(strMsg); // retry recursively
}
}
[TestCleanup]
public void TestCleanup()
{
// Use reflection to get the test method from the name, like "TestMethod1"
// so that we can invoke it.
var theTestMethod = this.
GetType().
GetMethods().
Where(m => m.Name == TestContext.TestName).
First();
for (int i = 0; i < 300; i++)
{
LogString("{0} {1}", theTestMethod.Name, i);
theTestMethod.Invoke(this, null);
}
}
[TestMethod]
public void TestMethod1()
{
var x = new WpfApplication1.MainWindow();
x.Content = new System.Windows.Controls.TextBlock()
{
Text = "Hi from my test"
};
x.Show();
}
}
}
</code>