Question: So you are using files in your application, how do you test easily how it behaves if the file access is denied ? Answer: Use declarative permissions to deny all access on the files
Ok that's a rough introduction, let me develop the idea. When your application relies on using resources such as files, TcpChannels, registry, etc... you should test how it behaves when the access to those resources is refused. The resource denial can be caused by a lot of external factors (loss of connectivity, etc...), lack of physical resources, .... or by denied security permissions. In other words, security permissions is an easy way to simulate various situations such as file access error, etc...
Let me illustrate this by implementing a test decorator for MbUnit that makes all file access diened.
Creating DenyFileIOAccess decorator
As all test decorators, we derive a new attribute class from MbUnit.Core.DecoratorPatternAttribute and a new IRunInvoker from DecoratorRunInvoker:
using System; using System.Security.Permissions; using System.Collections; using MbUnit.Core.Invokers; using MbUnit.Core.Framework; namespace MbUnit.Framework.Security { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false, Inherited =true)] public sealed class DenyFileIOAccessAttribute : DecoratorPatternAttribute { public override IRunInvoker GetInvoker(IRunInvoker invoker) { return new DenyFileIOAccessRunInvoker(invoker, this); } private sealed class DenyFileIOAccessRunInvoker : DecoratorRunInvoker { private DenyFileIOAccessAttribute attribute; public DenyFileIOAccessRunInvoker( IRunInvoker invoker, DenyFileIOAccessAttribute attribute) :base(invoker) { this.attribute = attribute; } [FileIOPermission(SecurityAction.Deny, AllFiles = FileIOPermissionAccess.AllAccess)] public override object Execute(Object o, IList args) { return this.Invoker.Execute(o, args); } } } }
The important thing to notice in the snippet is the FileIOPermission attribute that tags the execute method. All the code executed inside that method will have file IO denied. So since Invoker.Execute calls the test method, we have what we are looking for.
DenyFileIOAccessAttribute in action
Let put the new decorator in action. The following snippet shows a simple test that tries to create a new file. The execution log using TestDriven.NET is outputed below. As one can see, a SecurityException was raised by the framework when trying to open the file.... it's magic.
using System; using System.IO; using MbUnit.Framework; using MbUnit.Framework.Security; namespace MbUnit.Demo { [TestFixture] public class SecurityTest { [Test] [DenyFileIOAccess] public void SecureMethod() { using (StreamWriter writer = new StreamWriter("test.txt")) { writer.Write("we should not be here"); } } } } -- output ------ Test started: Assembly: MbUnit.Tests.1.1.dll ------ Info: Test Execution Info: Exploring MbUnit.Tests.1.1, Version=1.0.1791.9801, Culture=neutral, PublicKeyToken=null Info: MbUnit 2.22.0.0 Addin Info: Found 1 tests Info: [failure] SecurityTest.SecureMethod TestCase 'SecurityTest.SecureMethod' failed: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. System.Security.SecurityException Message: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. Source: mscorlib StackTrace: at System.Security.CodeAccessSecurityEngine.Check(PermissionToken permToken, CodeAccessPermission demand, StackCrawlMark& stackMark, Int32 checkFrames, Int32 unrestrictedOverride) at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap, StackCrawlMark& stackMark) at System.Security.CodeAccessPermission.Demand() at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize) at System.IO.StreamWriter..ctor(String path) \securitytest.cs(22,0): at MbUnit.Demo.SecurityTest.SecureMethod() 0 succeeded, 1 failed, 0 skipped, took 0.00 seconds. ---------------------- Done ----------------------
This methodology applies to the other types of permissions that can be found in the System.Security namespace.
Where can I get those ?
MbUnit will soon contain a few other of those attributes. Since it is a .Net 1.1 feature, they will be enclosed in the MbUnit.Framework.1.1.dll under the MbUnit.Security namespace.
Page rendered at Saturday, August 09, 2008 4:24:27 AM UTC
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.