Reputation: 3834
Here is my class which create new application instance:
public interface IApplicationInstanceProvider
{
bool CreateNewProcess();
}
public class ApplicationInstanceProvider : IApplicationInstanceProvider
{
public bool CreateNewProcess()
{
Process p = new Process();
p.StartInfo.FileName = System.Reflection.Assembly.GetEntryAssembly().Location;
return p.Start();
}
}
Here is my test case:
[TestMethod]
public void TestMethodForAppInstance()
{
IApplicationInstanceProvider provider = new ApplicationInstanceProvider();
bool isCreated = provider.CreateNewProcess();
Assert.AreEqual(isCreated,true);
}
Here is problem: System.Reflection.Assembly.GetEntryAssembly()
is null while test case execution. But it works fine while application running.
Please help!
Upvotes: 1
Views: 153
Reputation: 67080
You can't test your class as-is because you're executing an unknown new process in the test environment. It's not viable because:
Assembly.GetEntryAssembly()
will return null
.devenv.exe
, chrome.exe
or whatever.CreateNewProcess()
method does many things: it determines program to execute path and it run it. Moreover its return value tells caller if a new process has been started or an existing one has been reused. Too many things for a single method make it hard to test. Fortunately there are at least two approaches to make your code testable: create a specialized ApplicationInstanceProvider
class for testing or make a separate class for it.
Let's see FIRST METHOD:
public class ApplicationInstanceProvider : IApplicationInstanceProvider {
public bool CreateNewProcess() {
Process process = new Process();
process.StartInfo.FileName = ResolveApplicationPath();
return process.Start();
}
protected virtual string ResolveApplicationPath() {
return System.Reflection.Assembly.GetEntryAssembly().Location;
}
}
You'll create a derived class for testing:
sealed class TestApplicationInstanceProvider : ApplicationInstanceProvider {
protected override string ResolveApplicationPath() {
// path to your assembly or a well-known executable executable
// Like %WINDIR%\notepad.exe
return "...";
}
}
It'll be then used like this in your test method:
[TestMethod]
public void TestMethodForAppInstance() {
var provider = new TestApplicationInstanceProvider();
bool isCreated = provider.CreateNewProcess();
Assert.AreEqual(isCreated, true);
}
Note that you cannot test Assembly.GetEntryAssembly()
but you can test everything els. Note that now you're testing if you create a new process instance but you do not check you started right one; this will increase code coverage but you're actually testing almost nothing because Process.Start()
will always return true for executables (running process may be reused for documents). That's why you have to split CreateNewProcess()
responsabilities (not only for clarity but for testing). Do not forget to close process instance in your cleanup method after testing!
Let's see SECOND METHOD: second method is little bit more complicated but it's more versatile:
public interface IAssemblyResolver {
string GetEntryAssemblyPath();
}
public sealed class DefaultAssemblyResolver : IAssemblyResolver {
public string GetEntryAssemblyPath() {
return System.Reflection.Assembly.GetEntryAssembly().Location;
}
}
public class ApplicationInstanceProvider : IApplicationInstanceProvider {
public ApplicationInstanceProvider(IAssemblyResolver resolver) {
_resolver = resolver;
}
public bool CreateNewProcess() {
Process process = new Process();
process.StartInfo.FileName = _resolver.GetEntryAssemblyPath();
return process.Start();
}
private readonly IAssemblyResolver _resolver;
}
Now you have to create a mock for testing:
sealed class TestAssemblyResolver : IAssemblyResolver {
public string GetEntryAssemblyPath() {
// Return path of a well-known test application,
// for example an "empty" console application. You can also
// reuse it to, for example, return different error codes
return Assembly.Load(...);
}
}
Test method:
[TestMethod]
public void TestMethodForAppInstance() {
var resolver = new TestAssemblyResolver();
var provider = new ApplicationInstanceProvider(resolver);
bool isCreated = provider.CreateNewProcess();
Assert.AreEqual(isCreated, true);
}
What your fake application may look like?
static class Program {
static int Main(string[] args) {
if (args.Length == 0)
return 0;
return Int32.Parse(args[0]);
}
}
Upvotes: 1