FitSharp throws a ParseException when an object instance is stored in a slim symbol that is later used in a function call - parseexception

I store an instance in a slim symbol ($IT) but when I later try to use the instance in a function call I receive a fitSharp.Machine.Exception.ParseException.
I think the problem is that FitSharp tries to parse the argument instead of casting the object up to its interface.
I have the following class and interface (name space is MySlimTest)
public interface IObject {}
public class ConcreteObject: IObject
{
public string Name { get; set; }
public string Description { get; set; }
}
which I use in my slim fixture which includes the following method and the returned instance I store in a slim symbol.
public IObject CreateConcreteObjectWithNameAndDescription(string name, string description)
{
return new ConcreteObject() {Name = name, Description = description};
}
I call this method from a script table. When rendered after the test has run it looks like this:
$IT<-[MySlimTest.ConcreteObject] | create concrete object | with name | The initial name of the concrete object | and description | empty |
When I try to later use the instance stored in the slim symbol IT, the ParseException is thrown.
the method in the fixture is
public bool SetNameForInstance(string name, IObject obj)
{
return SetAttribute<ConcreteObject>(obj, concreteObject => concreteObject.Name = name);
}
this is used in the test table as
ensure | set name | John Cleese | for instance | $IT->[MySlimTest.ConcreteObject]
Workaround
Interestingly, if I change the method signature to use the implementation (ConcreteObject) instead of the interface then it works.
public bool SetNameForInstance(string name, ConcreteObject obj)
The complete example with fixture code follows as a fitnesse plain text wiki page:
!*> Environment
Linux 3.2.0-3-amd64 #1 SMP Mon Jul 23 02:45:17 UTC 2012 x86_64 GNU/Linux
Mono 2.10.8.1 (Debian 2.10.8.1-7) (64-bit)
glib-sharp 2.12.0.0
FitNesse (v20121220)
FitSharp release 2.2 for .net 4.0
*!
!*> Fixture Code
{{{
using System;
namespace MySlimTest
{
public interface IObject {}
public class ConcreteObject: IObject
{
public string Name { get; set; }
public string Description { get; set; }
}
public class SymbolTestFixture
{
public SymbolTestFixture ()
{
// Initialisation code here...
// Currently just a mock.
}
public IObject CreateConcreteObjectWithNameAndDescription(string name, string description)
{
return new ConcreteObject() {Name = name, Description = description};
}
public bool SetNameForInstance(string name, IObject obj)
{
return SetAttribute<ConcreteObject>(obj, concreteObject => concreteObject.Name = name);
}
public bool SetDescriptionForInstance(string description, ConcreteObject obj)
{
return SetAttribute<ConcreteObject>(obj, concreteObject => concreteObject.Description = description);
}
private bool SetAttribute<T>(IObject obj, Action<T> fun)
{
if (obj is T)
{
fun((T)obj);
return true;
}
return false;
}
}
}
}}}
*!
!3 Set-up of the test system
!***> Test system set-up
!define TEST_SYSTEM {slim}
!path /path/to/fixture/DLL/SlimTest.dll
!define COMMAND_PATTERN {%m -r fitSharp.Slim.Service.Runner,/path/to/fitSharp.dll %p}
!define TEST_RUNNER {/path/to/Runner.exe}
***!
!**> Usings
!|import |
|MySlimTest|
**!
!2 Scenarios
!|scenario|given concrete object |name |and |description |
|$IT= |create concrete object with name|#name|and description|#description|
!|scenario |given concrete object|name |
|given concrete object|#name |and|empty|
!|scenario|edit concrete object|instance |name to |name|and description to|description|
|ensure |set name |#name |for instance|$IT |
|ensure |set description |#description|for instance|$#instance |
!3 Test the ''slim symbol can hold object as parameter'' feature
!|script|symbol test fixture|
!|script |
|given concrete object|The initial name of the concrete object |
|edit concrete object |IT |name to |John Cleese |and description to|Yes, I am still indeed alive, contrary to rumour, and I am touring my one Cleese show. London.|

It's a bug. I forked, fixed and made a pull request on GitHub
https://github.com/jediwhale/fitsharp/pull/109

Related

Strongly-Typed values in specflow scenario

Is there any way to use Strongly-Typed values in Examples table of the scenario? (or alternative solution)
I'd like to know if I made a typo in userType column already during the coding (not during running the test).
UPDATED
file.feature
Scenario Outline: Scenario123
Given Create new user of type "<userType>"
Examples:
| userType |
| PlatinumUser |
| CommonUser |
steps.cs
[Given(#"Create new user of type ""(.*)""")]
public void CreateNewUser(UserTypeEnum userType)
{
// some code like e.g.:
MyUser user = new MyUser(userType);
//...
}
enum UserTypeEnum { CommonUser, PlatinumUser, Spectre }
Looks like its a StepArgumentTransformation that you are after?
https://github.com/techtalk/SpecFlow/wiki/Step-Argument-Conversions
Used somehow along these lines:
[Binding]
public class Transforms
{
[StepArgumentTransformation]
public UserTypeEnum UserTypeTransform(string UserType)
{
// return the string converted into the required Enum
}
}
The step binding will see that it requires a UserTypeEnum as a parameter so it will search for all the available Step Argument Transformations within any classes with the Binding attribute and use this method to perform the conversion.
Specflow supports accepting strongly typed enum values.
Though, the scenario sends it as text (case insensitive).
example:
Scenario: Some enum test
When I send enum "Second"
Then I get the second enum
public enum ChosenOption
{
First,
Second,
Third,
}
[When(#"I send enum ""(.*)""")]
public void WhenISendEnum(ChosenOption option)
{
_scenarioContext.Set(option, nameof(ChosenOption));
}
[Then(#"I get the second enum")]
public void ThenIGetTheSecondEnum()
{
var chosen = _scenarioContext.Get<ChosenOption>(nameof(ChosenOption));
chosen.Should().Be(ChosenOption.Second);
}

Audit Logging skips logging ID of tables with composite-primary key

I'm implementing Audit Logging 1.1 Grails Plugin to track the changes to my domain classes midway of our project implementation. These are an example domain object for our scenario:
Students need to answer questions. A question can ask for a single or a multiple answers.
class Question {
static auditable = true
Integer id
String content
static hasMany = [
answers: Answer
]
}
class Student {
static auditable = true
Integer id
String name
static hasMany = [
answers: Answer
]
}
class Answer implements Serializable {
static auditable = true
Integer sequence
String value
static belongsTo = [
student: Student,
question: Question
]
static mapping = {
id composite: ["student", "question", "sequence"]
}
}
Every time I perform insert/updates to any of these tables, the plugin fires an event and logs it to my AuditLog table. All DML are successfully logged as expected except for the Answer table. The problem is that the PERSISTED_OBJECT_ID is always null:
+----+---+------------+------------+---------------------+---------------+-----------+-----------+
| ID | … | CLASS_NAME | EVENT_NAME | PERSISTED_OBJECT_ID | PROPERTY_NAME | OLD_VALUE | NEW_VALUE |
+----+---+------------+------------+---------------------+---------------+-----------+-----------+
| … | … | Answer | UPDATE | | value | A | B |
| … | … | Answer | UPDATE | | value | B | A |
+----+---+------------+------------+---------------------+---------------+-----------+-----------+
I tried to include the logIds = true config but it still not persisting. Without that column, I cannot identify which Answer is updated by whom. I'm expecting that this would be the case of all the composite primary keys domain classes that I have.
What can I do to fix this?
I had the same issue with 2.x plugin version when my new/oldValue was domain class object. I decided it by overriding toString() method for domain which was saved as value. Try to override toString() for your "student" and "question" domain.
Update:
I didn't use composite key as Id in my project (it seems it's why your persistedObjectId is empty) and also I used 2.x version of plugin. My decision for problems which looks like your, may be it will help you :
I have domains:
class Manager {
}
class Customer {
}
class Item {
Manager manager
Customer customer
Item parentItem
BigDecimal qtyOnHand
}
I log changes of qtyOnHand and parentItem. With first everything is easy. For second (as I remember default value will be smth like this: "[id:15]Object123402"):
1. overrided toString()
public String toString() {
id
}
2. in configurations: logIds = false => plugin writes only id of object
Also I log managerId and customerId using "uri" for it (for simple it will be):
def getAuditLogUri = {
managerId as String
}
We can write all in uri and than past in beforeInsert:
class Item {
......
def getAuditLogUri = {
getAuditFields()
}
private Map getAuditFields() {
return [managerId: manager.id, customerId: customer.id]
}
....
}
class AuditLog{
String actor
String uri
String className
String persistedObjectId
String propertyName
String oldValue
String newValue
String managerId
String customerId
.....
def beforeInsert() {
getAdditionalFields(uri)
}
private void getAdditionalFields(String fields) {
Map map = Eval.me(fields)
managerId = map.managerId
customerId = map.customerId
}
Everything works in my project, I think the it will work the same for persistedObjectId-field

How to annotate interface property get and set with attributes in F#

How to translate the following COM interface to F#? I cannot figure out how to annotate get and set of a property.
Plus, for COM interop, do I need to annotate both a property itself and its get with DispId?
[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
private interface IWshShortcut
{
[DispId(0)]
string FullName { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0)] get; }
[DispId(0x3e8)]
string Arguments { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] set; }
[DispId(0x3ec)]
string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ec)] set; }
[DispId(0x3ee)]
int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; }
[DispId(0x3ef)]
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; }
[TypeLibFunc((short)0x40), DispId(0x7d0)]
void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
[DispId(0x7d1)]
void Save();
}
Here's a correct, though not literal, translation:
[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
[<DispId(0)>]
abstract member FullName : [<MarshalAs(UnmanagedType.BStr)>] string with get
[<DispId(0x3e8)>]
abstract member Arguments : [<MarshalAs(UnmanagedType.BStr)>] string with get, set
[<DispId(0x3ec)>]
abstract member RelativePath : [<MarshalAs(UnmanagedType.BStr)>] string with set
[<DispId(0x3ee)>]
abstract member WindowStyle : int with get, set
[<DispId(0x3ef)>]
abstract member WorkingDirectory : [<MarshalAs(UnmanagedType.BStr)>] string with get, set
[<DispId(0x7d0); TypeLibFunc(0x40s)>]
abstract member Load : [<MarshalAs(UnmanagedType.BStr)>] PathLink:string -> unit
[<DispId(0x7d1)>]
abstract member Save : unit -> unit
As you found, you cannot add attributes to an abstract property's underlying getter/setter methods in F#, only to the property itself, but it doesn't matter for this particular interface:
String properties with both get and set need the same MarshalAs for both anyway.
In is the default directionality for string parameters, so specifying it would be redundant anyway.
Applying DispId to the property getter as your C# code does is legal but pointless – while DispId can be applied to both methods and properties, and property getters and setters technically happen to be methods, the attribute only has an effect for the property itself.
N.b. because the CLR marshals string parameters for COM methods as BStrs by default, we can omit all the MarshalAs directives as well and make this look a bit more trim (albeit less explicit):
[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
[<DispId(0)>] abstract member FullName:string with get
[<DispId(0x3e8)>] abstract member Arguments:string with get, set
[<DispId(0x3ec)>] abstract member RelativePath:string with set
[<DispId(0x3ee)>] abstract member WindowStyle:int with get, set
[<DispId(0x3ef)>] abstract member WorkingDirectory:string with get, set
[<DispId(0x7d0); TypeLibFunc(0x40s)>] abstract member Load : PathLink:string -> unit
[<DispId(0x7d1)>] abstract member Save : unit -> unit
Of course, all of this applies to the C# implementation as well, so it can be similarly simplified:
[ComImport, Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"), TypeLibType((short)0x1040)]
private interface IWshShortcut
{
[DispId(0)] string FullName { get; }
[DispId(0x3e8)] string Arguments { get; set; }
[DispId(0x3ec)] string RelativePath { set; }
[DispId(0x3ee)] int WindowStyle { get; set; }
[DispId(0x3ef)] string WorkingDirectory { get; set; }
[DispId(0x7d0), TypeLibFunc((short)0x40)] void Load(string PathLink);
[DispId(0x7d1)] void Save();
}

multiple constructors and class inheritance in F#

I am having difficulty to convert following C# code to F#:
class Foo
{
public Foo() { }
public Foo(string name) { }
}
class Bar : Foo
{
public Bar() : base() { }
public Bar(string name) : base(name) { }
public string Name { get; set; }
}
I first tried following, but it is reporting error
Constructors for the type 'Bar' must directly or indirectly call its
implicit object constructor. Use a call to the implicit object
constructor instead of a record expression.
type Foo() =
new(name:string) = Foo()
type Bar() =
inherit Foo()
new(name:string) = { inherit Foo(name) }
member val Name:string = null with get, set
Then I tried following, but it is now reporting error on the auto property
'member val' definitions are only permitted in types with a primary
constructor. Consider adding arguments to your type definition"
type Foo() =
new(name:string) = Foo()
type Bar =
inherit Foo
new(name:string) = { inherit Foo(name) }
member val Name:string = null with get, set
If you want F# source code who compiles to precisely the same API as given by your C# code, the answer is as follows:
type Foo =
new() = {}
new(name:string) = { }
type Bar =
inherit Foo
[<DefaultValue>]
val mutable private name:string
new() = { inherit Foo() }
new(name) = { inherit Foo(name) }
member x.Name with get() = x.name and set v = x.name <- v
This compiles:
type Foo() =
new(name:string) = Foo()
type Bar(name : string) =
inherit Foo()
new() = Bar(null) // or whatever you want as a default.
member val Name:string = name with get, set
See Constructors (F#) and Inheritance (F#).
Looking at the decompilation, the C# would be (with attributes removed):
public class Bar : Program.Foo {
internal string Name#;
public string Name {
get {
return this.Name#;
}
set {
this.Name# = value;
}
}
public Bar(string name) {
this.Name# = name;
}
public Bar() : this(null) {
}
}
public class Foo {
public Foo() {
}
public Foo(string name) : this() {
}
}
If a class has a parameter list directly after its name (including ()), it has a primary constructor. Using it, any inherit declarations are placed only in this primary constructor, which comes directly after the class declaration and before any member declarations.
It is unclear what you are trying to achieve. The class Foo has a constructor taking a string argument, only to discard it. A (technically) valid, similar pair of classes would be this:
type Foo(name:string) =
member f.NameLength = name.Length
type Bar(initialName) = // WARNING: this will not end well
inherit Foo(initialName)
member val Name:string = initialName with get, set
But this is not sensible code. Foo will keep the initial name even if the name in Bar is changed. Bar.Name.Length returns the current name's length, while Bar.NameLength returns the initial name's length.
To keep the default constructor, one could add new () = Bar(null) (or the equivalent in Foo), but please note that null is considered an interop-only feature. It is not used in F# facing code; if possible, use the appropriate option type or an empty string respectively (depending on whether the string is just empty or doesn't exist at all).
Also, inheriting classes is discouraged in the F# component design guidelines -- for good reason. There are few use cases, but those usually involve a tiny base class and a derived class that is a perfect superset of it. It is far more common to compose types by using one class as a member of another.
I don't know how relevant this is, but here is an example of a class with default constructor and an additional constructor that uses it:
type Text500(text : string) =
do if text.Length > 500 then
invalidArg "text" "Text of this type cannot have a length above 500."
member t.Text = text
new () = Text500("")
This utilizes the primary constructor to verify input and has an additional, parameterless constructor that uses an empty string. (I'm not sure if the additional constructor would be useful in actual applications.)

Passing in the type of the declaring class for NLog using Autofac

Following on from this question I would like autofac to inject the type of the declaring object into the constructor of my NLog service, so that it can correctly log which type is logging entries.
My NLogService class looks like this...
public class NLogService : ILogService
{
private readonly Logger _logger;
public NLogService(Type t)
{
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
However it fails on app startup because it obviously cannot work out what to inject into the constructor of the NLogService with the following error...
None of the constructors found with
'Public binding flags' on type
'MyProduct.Domain.Services.Logging.NLogService'
can be invoked with the available
services and parameters: Cannot
resolve parameter 'System.Type t' of
constructor 'Void .ctor(System.Type)'.
So, my question is - how do i instruct autofac to inject the type of the calling class?
I tried this...
public NLogService(Type t)
{
var method = MethodBase.GetCurrentMethod();
Type consumingType = method.DeclaringType;
var consumerType = consumingType.FullName;
var consumerType = t.DeclaringType.FullName;
_logger = LogManager.GetLogger(consumerType);
}
But i just end up with MyProduct.Domain.Services.Logging.NLogService
What i want is the type of the class that is doing the actual logging.
i have already tried this suggestion and it didnt work for me either.
Could make your NLogService generic, i.e. NLogService<T> and use Autofac's open generics support?
Then you could do this:
public class NLogService<T> : ILogger<T>
{
private readonly Logger _logger;
public NLogService()
{
_logger = LogManager.GetLogger(typeof(T).FullName);
}
}
There is no real good way to do this with Autofac, because does not have support for 'context based injection' (which is what you are trying to do). There is a workaround, but it aint pretty...
What you can do is revert to property injection and define a base class or interface for that ILogService property. For instance, you can define the following interface:
public interface ILoggerContainer
{
public ILogService Logger { get; set; }
}
Now you can implement this interface on all types that need a logger:
public class Consumer : IConsumer, ILoggerContainer
{
public ILogService Logger { get; set; }
}
With this in place you can configure Autofac as follows:
builder.RegisterType<ILoggerContainer>()
.OnActivating(e =>
{
var type = typeof(LogService<>)
.MakeGenericType(e.Instance.GetType());
e.Instance.Logger = e.Context.Resolve(type);
});
Another workaround, that you may find cleaner is to inject an ILogger<T> with the same type as the type of the parent type:
public class Consumer : IConsumer
{
public Consumer(ILogger<Consumer> logger) { }
}
This makes the configuration much easier and prevents you from having to have a base class. Which one is most appropriate is up to you.
As I said, these are workarounds, but to be honest, you might need to reconsider your logging strategy in your application. Perhaps you are logging at too many places. In the applications I write there is hardly ever a need to log, and when I do, I write an logging message that is expressive enough so that there is no need to communicate the type that triggered the event. And when you log exception, you will always have a complete stack trace (and exception logging should almost only happen in the outer layer of your application and not within services anyway).
The following technique works well in our experience:
Create an attribute like below, which can be applied at class level or at the injection site:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class)]
public class LoggerAttribute : Attribute
{
public readonly string Name;
public LoggerAttribute(string name)
{
Name = name;
}
}
Create an Autofac module that you register with the ContainerBuilder:
public class LogInjectionModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
}
static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var typePreparing = e.Component.Activator.LimitType;
// By default, the name supplied to the logging instance is the name of the type in which it is being injected into.
string loggerName = typePreparing.FullName;
//If there is a class-level logger attribute, then promote its supplied name value instead as the logger name to use.
var loggerAttribute = (LoggerAttribute)typePreparing.GetCustomAttributes(typeof(LoggerAttribute), true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
e.Parameters = e.Parameters.Union(new Parameter[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof (Logger),
(p, i) =>
{
// If the parameter being injected has its own logger attribute, then promote its name value instead as the logger name to use.
loggerAttribute = (LoggerAttribute)
p.GetCustomAttributes(typeof(LoggerAttribute),true).FirstOrDefault();
if (loggerAttribute != null)
{
loggerName = loggerAttribute.Name;
}
// Return a new Logger instance for injection, parameterised with the most appropriate name which we have determined above.
return LogManager.GetLogger(loggerName);
}),
// Always make an unamed instance of Logger available for use in delegate-based registration e.g.: Register((c,p) => new Foo(p.TypedAs<Logger>())
new TypedParameter(typeof(Logger), LogManager.GetLogger(loggerName))
});
}
}
You can now inject a named Logger in any one of these ways depending on individual scenarios:
By default, the injected logger name will be given the full type name of the class it is injected into:
public class Foo
{
public Foo(Logger logger)
{
}
}
Use a constructor parameter [Logger] attribute to override the logger name:
public class Foo
{
public Foo([Logger("Meaningful Name")]Logger logger)
{
}
}
Use a class-level [Logger] attribute to set the same logger name override for all constructor overloads:
[Logger("Meaningful Name")]
public class Foo
{
public Foo(Logger logger, int something)
{
}
public Foo(Logger logger, int something, DateTime somethingElse)
{
}
}
Use constructor parameter [Logger] attributes on each constructor overload to set different logger names depending on the context of how you were constructed:
public class Foo
{
public Foo(Logger("Meaningful Name")]Logger logger, int something)
{
}
public Foo(Logger("Different Name")]Logger logger, int something, DateTime somethingElse)
{
}
}
IMPORTANT NOTE: If you register types to be resolved with logger constructor injection using Autofac's delegate registration, you MUST use the two parameter overload like so: Register((c,p) => new Foo(p.TypedAs<Logger>()).
Hope this helps!
It is possible to do this without generics.
However, please note that in Autofac 6.x, the resolution process has changed to use a resolve pipeline. This doesn't matter for most scenarios, but it does when you want to use the lifetime events like OnPreparing, etc. Most of the answers here on SO around overriding the Preparing event are very old and are now outdated. You can't override Preparing directly anymore.
There is an example on the Autofac documentation site doing this for log4net, and it works with NLog with only minor changes. Here is the basic idea:
public class Log4NetMiddleware : IResolveMiddleware
{
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
// Add our parameters.
context.ChangeParameters(context.Parameters.Union(
new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogManager.GetLogger(p.Member.DeclaringType)
),
}));
// Continue the resolve.
next(context);
// Has an instance been activated?
if (context.NewInstanceActivated)
{
var instanceType = context.Instance.GetType();
// Get all the injectable properties to set.
// If you wanted to ensure the properties were only UNSET properties,
// here's where you'd do it.
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);
// Set the properties located.
foreach (var propToSet in properties)
{
propToSet.SetValue(context.Instance, LogManager.GetLogger(instanceType), null);
}
}
}
}
Please also note that you have to understand how middleware works in Autofac. The documentation is a good place to start.

Resources