Skip to content

Commit

Permalink
FakeFaker checks callee if no module in placeholder (#120)
Browse files Browse the repository at this point in the history
* Added a method that adds the callee to the match.

* Added the StackTraceWrapper class.

This is used to check the call stack to see what class called the method.

* Injected the IStackTraceWrapper into the FakeFaker class.

* Renamed the file.

* Instead of throwing errors we just leave the string in place.

Updated the tests to make sure we don't throw errors anymore.

* F converts #'s to numbers.

* Updated the Parse method of the other fakers.
  • Loading branch information
mrstebo authored Mar 9, 2019
1 parent de9786f commit 691eff1
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 54 deletions.
6 changes: 1 addition & 5 deletions src/FakerDotNet/Fakers/AddressFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,7 @@ public string FullAddress()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{Address.$1}");

text = Regex.Replace(text, "#", m => _fakerContainer.Number.NonZeroDigit());

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
4 changes: 1 addition & 3 deletions src/FakerDotNet/Fakers/CompanyFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,7 @@ public string SouthAfricanTrustRegistrationNumber()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{Company.$1}");

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/FakerDotNet/Fakers/CompassFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ public string QuarterWindAzimuth()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{Compass.$1}");

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
Expand Down
93 changes: 70 additions & 23 deletions src/FakerDotNet/Fakers/FakeFaker.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using FakerDotNet.Wrappers;

namespace FakerDotNet.Fakers
{
Expand All @@ -13,48 +15,88 @@ public interface IFakeFaker
internal class FakeFaker : IFakeFaker
{
private readonly IFakerContainer _fakerContainer;
private readonly IStackTraceWrapper _stackTraceWrapper;

public FakeFaker(IFakerContainer fakerContainer)
: this(fakerContainer, new StackTraceWrapper())
{
}

internal FakeFaker(IFakerContainer fakerContainer, IStackTraceWrapper stackTraceWrapper)
{
_fakerContainer = fakerContainer;
_stackTraceWrapper = stackTraceWrapper;
}

public string F(string format)
{
if (string.IsNullOrEmpty(format)) return string.Empty;

var result = format;
FakerMatch match;
while ((match = ExtractMatchFrom(result)).Success)
{
var faker = GetFaker(match.Name);
var value = GetValue(faker, match.Method);
var start = result.Substring(0, match.Index);
var end = result.Substring(match.Index + match.Length);
var calleeFaker = GetCalleeFaker();
var placeholders = GetPlaceholders(format).ToArray();
var result = placeholders.Aggregate(format,
(current, placeholder) => Parse(current, GetFakerMatch(calleeFaker, placeholder, current)));

result = $"{start}{value}{end}";
}
return Numerify(result);
}

private string GetCalleeFaker()
{
var callee = _stackTraceWrapper.GetClassAtFrame(2) ?? "";

return result;
return Regex.Replace(callee, @"Faker$", "");
}

private static FakerMatch ExtractMatchFrom(string input)
private static IEnumerable<string> GetPlaceholders(string input)
{
const string pattern = @"\{(\w+)\.(\w+)\}";
const string pattern = @"\{(\w+)\}|\{(\w+)\.(\w+)\}";

return Regex.Matches(input, pattern)
.Cast<Match>()
.Where(x => x.Success)
.Select(x => x.Value);
}

private static FakerMatch GetFakerMatch(string calleeFaker, string placeholder, string input)
{
var pattern = Regex.Escape(placeholder);
var match = Regex.Match(input, pattern);

if (!match.Success) return new FakerMatch();

return match.Success
? new FakerMatch
{
Success = true,
Index = match.Index,
Length = match.Length,
Name = match.Groups[1].Value,
Method = match.Groups[2].Value
}
: new FakerMatch();
var split = match.Value.Replace("{", "").Replace("}", "").Split('.');
var name = split.Length > 1 ? split[0] : calleeFaker;
var method = split.Length > 1 ? split[1] : split[0];

return new FakerMatch
{
Success = true,
Index = match.Index,
Length = match.Length,
Name = name,
Method = method
};
}

private string Parse(string input, FakerMatch match)
{
try
{
if (!match.Success) return input;

var faker = GetFaker(match.Name);
var value = GetValue(faker, match.Method);
var start = input.Substring(0, match.Index);
var end = input.Substring(match.Index + match.Length);

return $"{start}{value}{end}";
}
catch
{
return input;
}
}

private PropertyInfo GetFaker(string name)
{
const BindingFlags flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;
Expand Down Expand Up @@ -82,6 +124,11 @@ private static object DefaultValue(ParameterInfo parameterInfo)
: null;
}

private string Numerify(string input)
{
return Regex.Replace(input, "#", m => _fakerContainer.Number.NonZeroDigit());
}

private struct FakerMatch
{
public bool Success;
Expand Down
4 changes: 1 addition & 3 deletions src/FakerDotNet/Fakers/HackerFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public string Ingverb()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{Hacker.$1}");

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
4 changes: 1 addition & 3 deletions src/FakerDotNet/Fakers/SlackEmojiFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ public string Emoji()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{SlackEmoji.$1}");

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
4 changes: 1 addition & 3 deletions src/FakerDotNet/Fakers/UniversityFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ public string GreekAlphabet()

private string Parse(string format)
{
var text = Regex.Replace(format, @"\{(\w+)\}", @"{University.$1}");

return _fakerContainer.Fake.F(text);
return _fakerContainer.Fake.F(format);
}
}
}
21 changes: 21 additions & 0 deletions src/FakerDotNet/Wrappers/StackTraceWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Diagnostics;

namespace FakerDotNet.Wrappers
{
internal interface IStackTraceWrapper
{
string GetClassAtFrame(int frameIndex);
}

internal class StackTraceWrapper : IStackTraceWrapper
{
public string GetClassAtFrame(int frameIndex)
{
return new StackTrace()
.GetFrame(frameIndex + 1)
?.GetMethod()
?.ReflectedType
?.Name;
}
}
}
81 changes: 70 additions & 11 deletions tests/FakerDotNet.Tests/Fakers/FakeFakerTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using FakeItEasy;
using FakerDotNet.Fakers;
using FakerDotNet.Wrappers;
using NUnit.Framework;

namespace FakerDotNet.Tests.Fakers
Expand All @@ -13,10 +13,12 @@ public class FakeFakerTests
public void SetUp()
{
_fakerContainer = A.Fake<IFakerContainer>();
_fakeFaker = new FakeFaker(_fakerContainer);
_stackTraceWrapper = A.Fake<IStackTraceWrapper>();
_fakeFaker = new FakeFaker(_fakerContainer, _stackTraceWrapper);
}

private IFakerContainer _fakerContainer;
private IStackTraceWrapper _stackTraceWrapper;
private IFakeFaker _fakeFaker;

[Test]
Expand Down Expand Up @@ -55,34 +57,91 @@ public void F_is_not_case_sensitive()
public void F_with_empty_string_returns_back_the_empty_string()
{
const string format = "";

Assert.AreEqual(format, _fakeFaker.F(format));
}

[Test]
public void F_with_invalid_faker_throws_FormatException()
public void F_with_invalid_faker_does_not_replace_text()
{
const string format = "{Unknown.Test}";

var ex = Assert.Throws<FormatException>(() => _fakeFaker.F(format));

Assert.AreEqual("Invalid module: Unknown", ex.Message);
Assert.AreEqual(format, _fakeFaker.F(format));
}

[Test]
public void F_with_invalid_method_throws_FormatException()
public void F_with_invalid_faker_method_does_not_replace_text()
{
const string format = "{Name.BadMethod}";

var ex = Assert.Throws<FormatException>(() => _fakeFaker.F(format));

Assert.AreEqual("Invalid method: Name.BadMethod", ex.Message);
Assert.AreEqual(format, _fakeFaker.F(format));
}

[Test]
public void F_with_null_returns_an_empty_string()
{
Assert.AreEqual(string.Empty, _fakeFaker.F(null));
}

[Test]
public void F_with_only_method_name_calls_method_from_callee_module()
{
const string format = "{FirstName}";

A.CallTo(() => _stackTraceWrapper.GetClassAtFrame(2))
.Returns("Name");
A.CallTo(() => _fakerContainer.Name.FirstName())
.Returns("John");

Assert.AreEqual("John", _fakeFaker.F(format));
}

[Test]
public void F_with_only_method_name_and_module_does_not_exist_does_not_replace_text()
{
const string format = "{FirstName}";

A.CallTo(() => _stackTraceWrapper.GetClassAtFrame(2))
.Returns("NonExistentFaker");

Assert.AreEqual(format, _fakeFaker.F(format));
}

[Test]
public void F_with_only_method_name_and_method_does_not_exist_does_not_replace_text()
{
const string format = "{BadMethod}";

A.CallTo(() => _stackTraceWrapper.GetClassAtFrame(2))
.Returns("Name");

Assert.AreEqual(format, _fakeFaker.F(format));
}

[Test]
public void F_handles_only_method_name_and_faker_and_method_name_placeholders()
{
const string format = "{FirstName} {Name.LastName}";

A.CallTo(() => _stackTraceWrapper.GetClassAtFrame(2))
.Returns("Name");
A.CallTo(() => _fakerContainer.Name.FirstName())
.Returns("John");
A.CallTo(() => _fakerContainer.Name.LastName())
.Returns("Smith");

Assert.AreEqual("John Smith", _fakeFaker.F(format));
}

[Test]
public void F_converts_hashes_to_numbers()
{
const string format = "My Number is ####";

A.CallTo(() => _fakerContainer.Number.NonZeroDigit())
.ReturnsNextFromSequence("5", "3", "1", "8");

Assert.AreEqual("My Number is 5318", _fakeFaker.F(format));
}
}
}
34 changes: 34 additions & 0 deletions tests/FakerDotNet.Tests/Wrappers/StackTraceWrapperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using FakerDotNet.Wrappers;
using NUnit.Framework;

namespace FakerDotNet.Tests.Wrappers
{
[TestFixture]
[Parallelizable]
public class StackTraceWrapperTests
{
[SetUp]
public void SetUp()
{
_stackTraceWrapper = new StackTraceWrapper();
}

private IStackTraceWrapper _stackTraceWrapper;

[Test]
public void GetClassAtFrame_returns_the_class_at_a_frame_index_plus_one()
{
var result = _stackTraceWrapper.GetClassAtFrame(0);

Assert.AreEqual("StackTraceWrapperTests", result);
}

[Test]
public void GetClassAtFrame_returns_null_if_no_class_at_the_specified_frame_index()
{
var result = _stackTraceWrapper.GetClassAtFrame(99);

Assert.IsNull(result);
}
}
}

0 comments on commit 691eff1

Please sign in to comment.