Как автоматизировать тестирование кода среднего доверия

Я хотел бы написать автоматизированные тесты, которые выполняются со средним доверием и терпят неудачу, если они требуют полного доверия.

Я пишу библиотеку, в которой некоторые функции доступны только в сценариях полного доверия, и я хочу убедиться, что код, который я хочу запустить при среднем доверии, будет работать нормально. Если вы также хотите знать, что если я изменю класс, требующий полного доверия, мои тесты не пройдут.

Я попытался создать еще один AppDomain и загрузить средний уровень доверия PolicyLevel, но я всегда получаю сообщение об ошибке сборки, или его зависимость не может быть загружена при попытке запустить перекрестный обратный вызов AppDomain.

Есть ли способ осуществить это?

ОБНОВЛЕНИЕ: основанные ответы, вот что я имею. Обратите внимание, что тестируемый класс должен расширять MarshalByRefObject. Это очень ограничивает, но я не вижу способа обойти это.

using System;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using Xunit;

namespace PartialTrustTest
{
    [Serializable]
    public class ClassUnderTest : MarshalByRefObject
    {
        public void PartialTrustSuccess()
        {
            Console.WriteLine( "partial trust success #1" );
        }

        public void PartialTrustFailure()
        {
            FieldInfo fi = typeof (Int32).GetField( "m_value", BindingFlags.Instance | BindingFlags.NonPublic );
            object value = fi.GetValue( 1 );
            Console.WriteLine( "value: {0}", value );
        }
    }

    public class Test
    {
        [Fact]
        public void MediumTrustWithExternalClass()
        {
            // ClassUnderTest must extend MarshalByRefObject
            var classUnderTest = MediumTrustContext.Create<ClassUnderTest>();

            classUnderTest.PartialTrustSuccess();
            Assert.Throws<FieldAccessException>( classUnderTest.PartialTrustFailure );
        }
    }

    internal static class MediumTrustContext
    {
        public static T Create<T>()
        {
            AppDomain appDomain = CreatePartialTrustDomain();
            var t = (T) appDomain.CreateInstanceAndUnwrap( typeof (T).Assembly.FullName, typeof (T).FullName );
            return t;
        }

        public static AppDomain CreatePartialTrustDomain()
        {
            var setup = new AppDomainSetup {ApplicationBase = AppDomain.CurrentDomain.BaseDirectory};
            var permissions = new PermissionSet( null );
            permissions.AddPermission( new SecurityPermission( SecurityPermissionFlag.Execution ) );
            permissions.AddPermission( new ReflectionPermission( ReflectionPermissionFlag.RestrictedMemberAccess ) );
            return AppDomain.CreateDomain( "Partial Trust AppDomain: " + DateTime.Now.Ticks, null, setup, permissions );
        }
    }
}

5 ответов

Решение

Я только что опубликовал статью под названием " Частичное доверие" с xUnit.net. В нем подробно описывается основанная на xUnit.net инфраструктура, которую мы используем в команде Entity Framework для выполнения кода с частичным доверием.

Вот пример его использования.

public class SomeTests : MarshalByRefObject
{
    [PartialTrustFact]
    public void Partial_trust_test1()
    {
        // Runs in medium trust
    }
}

// Or...

[PartialTrustFixture]
public class MoreTests : MarshalByRefObject
{
    [Fact]
    public void Another_partial_trust_test()
    {
        // Runs in medium trust
    }
}

Бесстыдно украденный из " Как разместить частичную доверительную среду" - # 7, но повторно реализованный (вместе с простым контрольным примером) в F# просто для удовольствия:-)

open System
open System.Reflection
open System.Security
open System.Security.Permissions
open System.Security.Policy

type Program() =
    inherit System.MarshalByRefObject()
    member x.PartialTrustSuccess() =
        Console.WriteLine("foo")
    member x.PartialTrustFailure() =
        let field = typeof<Int32>.GetField("m_value", BindingFlags.Instance ||| BindingFlags.NonPublic)
        let value = field.GetValue(1)
        Console.WriteLine("value: {0}", value)

[<EntryPoint>]
let main _ =
    let appDomain =
        let setup = AppDomainSetup(ApplicationBase = AppDomain.CurrentDomain.BaseDirectory)
        let permissions = PermissionSet(null)
        permissions.AddPermission(SecurityPermission(SecurityPermissionFlag.Execution)) |> ignore
        permissions.AddPermission(ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess)) |> ignore
        AppDomain.CreateDomain("Partial Trust AppDomain", null, setup, permissions)

    let program = appDomain.CreateInstanceAndUnwrap(
                      typeof<Program>.Assembly.FullName,
                      typeof<Program>.FullName) :?> Program

    program.PartialTrustSuccess()

    try
        program.PartialTrustFailure()
        Console.Error.WriteLine("partial trust test failed")
    with
        | :? FieldAccessException -> ()

    0

И версия на C#:

using System;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

namespace PartialTrustTest
{
    internal class Program : MarshalByRefObject
    {
        public void PartialTrustSuccess()
        {
            Console.WriteLine("partial trust success #1");
        }

        public void PartialTrustFailure()
        {
            FieldInfo fi = typeof(Int32).GetField("m_value", BindingFlags.Instance | BindingFlags.NonPublic);
            object value = fi.GetValue(1);
            Console.WriteLine("value: {0}", value);
        }

        private static AppDomain CreatePartialTrustDomain()
        {
            AppDomainSetup setup = new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
            PermissionSet permissions = new PermissionSet(null);
            permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
            return AppDomain.CreateDomain("Partial Trust AppDomain", null, setup, permissions);
        }

        static void Main(string[] args)
        {
            AppDomain appDomain = CreatePartialTrustDomain();

            Program program = (Program)appDomain.CreateInstanceAndUnwrap(
                typeof(Program).Assembly.FullName,
                typeof(Program).FullName);

            program.PartialTrustSuccess();

            try
            {
                program.PartialTrustFailure();
                Console.Error.WriteLine("!!! partial trust test failed");
            }
            catch (FieldAccessException)
            {
                Console.WriteLine("partial trust success #2");
            }
        }
    }
}
 C: \ Temp\PartialTrustTest\ Bin\Debug>PartialTrustTest.exe
 частичное доверие успех #1
 частичное доверие успех № 2

Я опубликовал пост в блоге из трех частей о модульном тестировании в Medium Trust

Я раскручиваю альтернативный AppDomain аналогично некоторым ответам здесь, но продолжу его, используя MarshalByRefObject для вызова метода test в другом AppDomain, что означает, что ваши тестовые классы не должны реализовывать MarshalByRefObject

Третья часть (содержащая ссылки на другие части) находится здесь http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/

Я никогда не пытался сделать это, но в целом для обработки ошибок загрузки сборки из пользовательских доменов приложений можно использовать событие AppDomain.AssemblyResolve.

Хотя это совершенно не связано, вот пример использования AppDomain.AssemblyResolve.

Ответ зависит от того, что делает ваш код, когда кто-то со средним уровнем доверия пытается получить доступ к функции полного доверия. Я предполагаю, что какое-то исключение будет брошено.

В этом случае напишите модульный тест, который выполняется в контексте среднего доверия, пытается получить доступ к функции полного доверия и ожидает, что будет сгенерировано исключение. Если вы никогда не писали тест, подобный этому, один из распространенных способов сделать это, который поддержит большинство сред тестирования, это:

testMediumTrustUserWontAccessFeatureX()
{
    // set up the context of your test ...

    try
    {
        accessFullTrustFeature();
        fail("Test failed - Medium trust user can access full trust feature");
    }
    catch( SomeKindOfException e )
    {
        // Success - feature was denied to the untrusted user 
    }
}

Если исключение обнаружено, это означает, что ваш ненадежный пользователь не получил доступа к этой функции (и тест прошел), но если исключение не было обнаружено, тест завершается неудачно (мы ожидали исключение и не получили его).

Это псевдокод Java-esque, но этот шаблон должен работать на любом языке, который вы используете, предполагая, что он поддерживает обработку исключений.

Другие вопросы по тегам