I’m delighted with the way Unmockable evolved from a red-green-refactor perspective. It’s packed with principles and patterns, but I think not over-engineered (yet). If you haven’t seen it, please check it out!
ArgMatcherFactory creates a new argument matcher when the incoming type matches the
Arg class. This class has two placeholder methods for which are specific matchers created, and an
InvalidOperationException is thrown otherwise.
public static IArgumentMatcher? Create(Expression arg) =>
when call.Method.DeclaringType == typeof(Arg) =>
throw new InvalidOperationException()
_ => null
Compared to the original code I think it looks pretty neat and expressive.
public static IArgumentMatcher Create(Expression arg)
if (arg is MethodCallExpression call &&
call.Method.DeclaringType == typeof(Arg))
return new IgnoreArgument();
return new EqualsArgument(call.Arguments);
throw new NotImplementedException();
Score: +1. Great feature, expressions everywhere ❤.
Nullable reference types
This is probably the most outstanding feature of C# 8 and at the same time, the least appealing from a semantic perspective. It took me a while to understand what the compiler was telling me because it only tells you what could be wrong. But after I figured out, I think my source code improved immensely.
One small example on the default
Equals method and the
public bool Equals(CollectionArgument? other) =>
other != null && _collection.SequenceEqual(other._collection);
public override bool Equals(object obj) =>
Equals(obj as CollectionArgument);
In my original code, the type check was only performed on the
Equals(object) method so the
Equals(CollectionArgument could indeed be null.
public bool Equals(CollectionArgument other) =>
_collection.SequenceEqual(other._collection); public override bool Equals(object obj) =>
obj is CollectionArgument other && Equals(other);
null-check did not satisfy the compiler immediately. It looks like I had to add the
? to the parameter type to instruct the compiler I was aware the value could indeed be null and I’m taking care of that.
In my case, I could probably have come away with the
!. operator, but in the end, I’m happy with the improved safeguards.
Score: +infinite. The impact of this feature is unmeasurable.
Default interface members
Unfortunately, or luckily, this feature is only available if you target the newer runtime. Something I didn’t want to do yet because I want to support netstandard1.3 and the lowest runtime possible.
I was refactoring the ResultContext and at some point, almost all of the INextResult implementations shared a common implementation, so it felt like an almost perfect fit for default implementation. Almost since I have control over the source and the interface is not exposed to the outside world.
After several hard resets, I ended up with a significant refactor where the complete context disappeared and the INextResult evolved into IResult with a slimmer interface and specific implementations. Big props to Pim Elshoff for showing me a glimpse of DDD on Value Objects and Entities.
Score: unknown. I haven’t decided yet.
I’m very eager to try this one out, but unfortunately, there isn’t a single async method exposed in this framework. Luckily there are a lot in another framework I’m involved in, so stay tuned.
Score: +1. Superficial, but I’m sure we needed this.