2

我正在开发一个 C# 应用程序,该应用程序应该检查其他 C# 可执行文件并通过单元测试断言它们公开的接口的某些属性。(就上下文而言,这是一个 CS 课程的评分应用程序;任务是从文本文件中解析数字范围并根据固定接口返回它们。)

到目前为止,我已经设法:

  • 将可执行文件作为变量加载assembly,使用Assembly.LoadFrom(string)
  • 获取相关接口的类型,使用assembly.GetType(string)
  • 找到接口的实现类型,再次使用assembly.getType(string)
  • 将实现类型实例化为dynamic对象,使用type.GetConstructor(Type[])andconstructor.Invoke(Object[])

此时,我有一个dynamic对象loader,我知道它实现了我正在测试的接口。我想调用其中一个接口方法obj,所以我运行:

dynamic rangeSet = loader.GetRangeSetFromFile (inputFile); // inputFile is a string

这将引发InvalidCastException以下跟踪:

System.InvalidCastException : Cannot cast from source type to destination type.
at SwapAssignment3.Implementations.RangeLoaderAdapter.GetRangeSetFromFile (string) <IL 0x0001e, 0x00066>
at (wrapper dynamic-method) object.CallSite.Target (System.Runtime.CompilerServices.Closure,System.Runtime.CompilerServices.CallSite,object,string) <IL 0x00036, 0x0007b>
at System.Dynamic.UpdateDelegates.UpdateAndExecute2<object, string, object> (System.Runtime.CompilerServices.CallSite,object,string) <0x003cf>
at AssignmentTests.R3Test.TestLoadingViaInterface () [0x00054] in /Users/tim/Dropbox/Courses/CSSE375-TA/AssignmentTests/AssignmentTests/R3Test.cs:82
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <0x00003>
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <IL 0x000db, 0x00147>

为什么会抛出这个错误?运行有问题的可执行文件本身就可以正常工作;此错误仅在我的测试应用程序的上下文中引发。该GetRangeSetFromFile方法的主体如下:

public IRangeSet GetRangeSetFromFile(string filePath)
{
    var newRange = new RangeStorage();
    _fileProcessor.ProcessFile(filePath);
    newRange.StoreElements((List<IRange>) _fileProcessor.GetStorage());
    return newRange;
}

我有充分的理由相信(从程序输出中,除其他外)演员错误是从第三行引发的,与List<IRange>演员; 但是,由于跟踪提供了 IL 位置,因此我不能 100% 确定这一点,而且我不知道为什么该演员一开始会失败,因为如果程序自行运行(在我的测试员)。

我的主要问题是:为什么会引发这个演员错误,我该如何避免它?

编辑:根据要求,测试代码如下:

Type interfaceType = assembly.GetType("IRangeLoader");
List<Type> implementingTypes = new List<Type> (assembly.GetTypes ())
                                        .FindAll ((Type t) => t.IsClass)
                                        .FindAll ((Type t) => (new List<Type> (t.GetInterfaces ())).Contains (interfaceType));
Type implementingType = implementingTypes[0];
ConstructorInfo ctor = implementationType.GetConstructor (new Type[] {});
dynamic loader = ctor.Invoke (new Object[] {});
dynamic rangeSet = loader.GetRangeSetFromFile ("sample range.txt");
4

2 回答 2

1

我认为这是一个好主意,但看起来你在这里过于偏袒学生。

您可以提供一个程序集(例如CSxxxx.Interfaces.dll),要求您的学生参考和实施。从那里加载实现非常简单。

您还可以提供一套测试,如果它们全部通过,它们可能会产生一个 C 左右的结果。然后你的套件将测试一切。如果您有点狡猾并且可以指望您的一些学生扩展给定的测试,那么您可以针对所有其他实现运行 StudentX 的测试,并为破坏其他实现给予奖励积分。

于 2012-05-03T20:16:04.637 回答
1

好的,以下代码似乎可以在另一个程序集中调用该方法:

Assembly testAssembly = Assembly.LoadFile(<path>);

var interfaceType = testAssembly.GetTypes().Where(x => x.Name == "ISampleInterface").FirstOrDefault();

if(interfaceType != null)
{
    var implementingType = testAssembly.GetTypes().Where(typ => type.GetInterfaces().Any(iface => iface == interfaceType)).FirstOrDefault();

    if(implementingType != null)
    {
        dynamic obj = Activator.CreateInstance(implementingType);

        dynamic result = obj.SampleInterfaceMethod();

        Console.WriteLine(result);
    }
}

尝试使用其中的一些。我能够调用该对象,然后从该方法中获取结果。

于 2012-05-03T18:27:54.947 回答