Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Feature/handle users in multiple experiments #1

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
48 changes: 45 additions & 3 deletions Gibe.AbTest.Tests/AbTestRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,53 @@ namespace Gibe.AbTest.Tests
[TestFixture]
public class AbTestRepositoryTests
{
private const string ValidExperimentId = "sq0S2zdKQhWEmBc6sQ4sfQ";
private const string InvalidExperimentId = "NotRealId";

[Test]
public void Test()
public void GetEnabledExperiments_Returns_All_Enabled_Experiments()
{
var repo = new AbTestRepository(new DefaultDatabaseProvider("GibeCommerce"));
var experiments = repo.GetExperiments().ToArray();
var experiments = Repo().GetEnabledExperiments().ToArray();

Assert.That(experiments.Length, Is.EqualTo(1));
}

[Test]
public void GetExperiment_Returns_Experiment_When_Id_Exists()
{
var experiment = Repo().GetExperiment(ValidExperimentId);

Assert.That(experiment, Is.Not.Null);
Assert.That(experiment.Id, Is.EqualTo(ValidExperimentId));
}

[Test]
public void GetExperiment_Returns_Null_When_Id_Does_Not_Exist()
{
var experiment = Repo().GetExperiment(InvalidExperimentId);

Assert.That(experiment, Is.Null);
}

[Test]
public void GetVariations_Returns_All_Variations_For_Experiment_When_Id_Exists()
{
var variations = Repo().GetVariations(ValidExperimentId).ToArray();

Assert.That(variations.Count(), Is.EqualTo(2));
Assert.That(variations.All(v => v.ExperimentId == ValidExperimentId), Is.True);
}

[Test]
public void GetVariations_Returns_Empty_Enumerable_For_Experiment_When_Id_Does_Not_Exist()
{
var variations = Repo().GetVariations(InvalidExperimentId);

Assert.That(variations.Count(), Is.EqualTo(0));
}

private IAbTestRepository Repo() => new AbTestRepository(new DefaultDatabaseProvider("GibeCommerce"));


}
}
106 changes: 80 additions & 26 deletions Gibe.AbTest.Tests/AbTestTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace Gibe.AbTest.Tests
{
Expand All @@ -9,70 +10,123 @@ public class AbTestTests
{
private const string MobileUserAgent = "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0";
private const string DesktopUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0";
private IAbTestingService _abTestingService;
private IAbTestingService _simpleAbTestingService;

[SetUp]
public void Setup()
{
_abTestingService = new FakeAbTestingService(new List<Experiment>
{
new Experiment("Ex1", "Exp1", "Experiment 1", 1, true, DateTime.Now, null,
new []{
new Variation(1, 0, 1, true, "{Exp1:'Variant 1'}", "Exp1", false),
new Variation(2, 1, 1, true, "{Exp1:'Variant 2'}", "Exp1", false)
}),
new Experiment("Ex2", "Exp2", "Experiment 2", 1, true, DateTime.Now, null,
new []{
new Variation(3, 0, 1, true, "{Exp2:'Variant 1'}", "Exp2", false),
new Variation(4, 1, 1, true, "{Exp2:'Variant 2'}", "Exp2", true)
}),
new Experiment("Ex3", "Exp3", "Experiment 3", 1, false, DateTime.Now, null,
new []{
new Variation(5, 0, 1, true, "{Exp3:'Variant 1'}", "Exp3", true),
new Variation(6, 1, 1, true, "{Exp3:'Variant 2'}", "Exp3", true)
})
});

_simpleAbTestingService = new FakeAbTestingService(new List<Experiment>
{
new Experiment("Ex1", "Exp1", "Experiment 1", 1, true, DateTime.Now, null,
new []{
new Variation(1, 0, 1, true, "{Exp1:'Variant 1'}", "Exp1", false),
new Variation(2, 1, 1, true, "{Exp1:'Variant 2'}", "Exp1", false)
})
});
}

[Test]
public void AssignVariation_assigns_first_experiment_variation_when_random_number_is_0()
public void AssignVariation_Assigns_First_Experiment_Variation_When_Random_Number_Is_0()
{
var fakeAbTestingService = new FakeAbTestingService();
var abTest = new AbTest(_simpleAbTestingService, new FakeRandomNumber(new [] { 0, 0 }));

var abTest = new AbTest(fakeAbTestingService, new FakeRandomNumber(new [] { 0, 0 }));
var variation = abTest.AssignRandomVariation(DesktopUserAgent);
Assert.AreEqual(_simpleAbTestingService.GetEnabledExperiments().First().Variations.First().Id, variation.Id);
}

var variation = abTest.AssignVariation(MobileUserAgent);
[Test]
public void AssignVariation_Assigns_Second_Experiment_Variation_When_Random_Number_Is_1()
{

Assert.AreEqual(fakeAbTestingService.GetExperiments().First().Variations.First().Id, variation.Id);
var abTest = new AbTest(_simpleAbTestingService, new FakeRandomNumber(new[] { 1, 1 }));

var variation = abTest.AssignRandomVariation(DesktopUserAgent);

Assert.AreEqual(_simpleAbTestingService.GetEnabledExperiments().First().Variations.ElementAt(1).Id, variation.Id);
}

[Test]
public void AssignVariation_assigns_second_experiment_variation_when_random_number_is_1()
public void AssignVariation_Assigns_Second_Experiment_Variation_When_Random_Number_Is_2()
{
var fakeAbTestingService = new FakeAbTestingService();
var abTest = new AbTest(_simpleAbTestingService, new FakeRandomNumber(new[] { 2, 2 }));

var abTest = new AbTest(fakeAbTestingService, new FakeRandomNumber(new[] { 1, 1 }));
var variation = abTest.AssignRandomVariation(DesktopUserAgent);
Assert.AreEqual(_simpleAbTestingService.GetEnabledExperiments().First().Variations.ElementAt(1).Id, variation.Id);
}

[Test]
public void AssignVariation_Assigns_Given_Experiment_First_Variation()
{
var abTest = new AbTest(_abTestingService, new FakeRandomNumber(new[] { 0, 0, 0 }));

var variation = abTest.AssignVariation(MobileUserAgent);
var variation = abTest.AssignVariationByExperimentKey("Exp1");

Assert.AreEqual(fakeAbTestingService.GetExperiments().ElementAt(1).Variations.ElementAt(1).Id, variation.Id);
Assert.That(_abTestingService.GetEnabledExperiments().First(x => x.Key == "Exp1").Variations.First().Id, Is.EqualTo(variation.Id));
}

[Test]
public void AssignVariation_does_not_assign_mobile_user_to_desktop_variant()
{
var fakeAbTestingService = new FakeAbTestingService();

var abTest = new AbTest(fakeAbTestingService, new FakeRandomNumber(new[] { 1, 1 }));
var abTest = new AbTest(_abTestingService, new FakeRandomNumber(new[] { 1, 1, 1 }));

var variation = abTest.AssignVariation(MobileUserAgent);
var variation = abTest.AssignRandomVariation(MobileUserAgent);

Assert.IsFalse(variation.DesktopOnly);
}

[Test]
public void AssignVariation_assigns_desktop_user_to_desktop_variant()
{
var fakeAbTestingService = new FakeAbTestingService();

var abTest = new AbTest(fakeAbTestingService, new FakeRandomNumber(new[] { 1, 1 }));
var abTest = new AbTest(_abTestingService, new FakeRandomNumber(new[] { 1, 1, 1 }));

var variation = abTest.AssignVariation(DesktopUserAgent);
var variation = abTest.AssignRandomVariation(DesktopUserAgent);

Assert.IsTrue(variation.DesktopOnly);
}

[Test]
public void AssignVariations_Assigns_First_Experiment_Variation_From_Each_Enabled_Experiment()
{
var abTest = new AbTest(_abTestingService, new FakeRandomNumber(new[] { 0, 0, 0 }));

var variations = abTest.AllCurrentVariations().ToList();

Assert.That(_abTestingService.GetEnabledExperiments().Where(e => e.Enabled).Select(e => e.Variations.First().Id), Is.EqualTo(variations.Select(v => v.Id)));
Assert.That(variations.Count, Is.EqualTo(2));
}

[Test]
public void GetAssignedVariation_returns_variation_from_AbTestingService()
{
const string experimentId = "ABC";
const string experimentId = "Exp4";
const int variationNo = 1;

var fakeAbTestingService = new FakeAbTestingService();
var abTest = new AbTest(_abTestingService, new FakeRandomNumber(new int[] {}));

var abTest = new AbTest(fakeAbTestingService, new FakeRandomNumber(new int[] {}));

var variation = abTest.GetAssignedVariation(experimentId, variationNo);
var variation = abTest.Variation(experimentId, variationNo);

Assert.AreEqual(experimentId, variation.ExperimentId);
Assert.AreEqual(variationNo, variation.VariationNumber);

}
}
}
2 changes: 1 addition & 1 deletion Gibe.AbTest.Tests/AbTestingServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void GetExperiments_Returns_Empty_Experiment_When_No_Experiments()
{
var fakeAbTestingService = new FakeAbTestRepository(new List<ExperimentDto>(), new List<VariationDto>());

var experiments = Service(fakeAbTestingService).GetExperiments();
var experiments = Service(fakeAbTestingService).GetEnabledExperiments();

var expected = new Experiment(new ExperimentDto
{
Expand Down
2 changes: 1 addition & 1 deletion Gibe.AbTest.Tests/App.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="GibeCommerce" connectionString="Data Source=cartman2.gibe.local;Initial Catalog=TapWarehouse;Integrated Security=true" providerName="System.Data.SqlClient" />
<add name="GibeCommerce" connectionString="Data Source=cartman2.office.gibe.digital;Initial Catalog=TapWarehouse;Integrated Security=true" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
149 changes: 149 additions & 0 deletions Gibe.AbTest.Tests/ExperimentServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System.Collections.Generic;
using System.Linq;
using Gibe.Cookies;
using Moq;
using NUnit.Framework;

namespace Gibe.AbTest.Tests
{
[TestFixture]
public class ExperimentServiceTests
{
private const string CookieKey = "GCEXP";

private Mock<ICookieService> _cookieService;
private IAbTest _abTest;

[SetUp]
public void Setup()
{
_cookieService = new Mock<ICookieService>();
_abTest = new FakeAbTest(
new List<Variation>{
new Variation(1, 0, 1,true,"{Test:'test1'}", "vapBwUPvTEuGcEVEKThGCA", false),
new Variation(2, 1, 1,true,"{Test:'test1'}", "vapBwUPvTEuGcEVEKThGCA", false),
new Variation(3, 0, 1,true,"{Test:'test2'}", "vapBwUPvTEuGcEVEKThGCB", false),
new Variation(4, 1, 1,true,"{Test:'test2'}", "vapBwUPvTEuGcEVEKThGCB", false),
new Variation(5, 0, 1,true,"{Test:'test3'}", "vapBwUPvTEuGcEVEKThGCC", false),
new Variation(6, 1, 1,true,"{Test:'test3'}", "vapBwUPvTEuGcEVEKThGCC", false)
});
}

private IExperimentService ExperimentService()
{
return new DefaultExperimentService(_cookieService.Object, _abTest, new ExperimentCookieValueFactory(_abTest));
}

[Test]
public void IsCurrentUserInExperiment_Returns_True_When_Cookie_Contains_Experiment()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1");

var result = ExperimentService().IsCurrentUserInExperiment();

Assert.That(result, Is.True);
}

[Test]
public void IsCurrentUserInExperiment_Returns_False_When_No_Experiment_Cookie_Found()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("");

var result = ExperimentService().IsCurrentUserInExperiment();

Assert.That(result, Is.False);
}

[Test]
public void CurrentUserVariations_Returns_Enumerable_Of_Variations_Based_On_The_Cookie_Value()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1");

var results = ExperimentService().CurrentUserVariations();

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1");
}

[Test]
public void CurrentUserVariation_Returns_Variation_For_ExperiementId_Based_On_The_Cookie_Value()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1");

var results = ExperimentService().CurrentUserVariation("vapBwUPvTEuGcEVEKThGCA");

AssertVariations(new []{results}, "vapBwUPvTEuGcEVEKThGCA~0");
}

[Test]
public void AssignUserVariations_Assigns_User_To_Random_Variations_For_Each_Experiment()
{

var results = ExperimentService().AssignUserVariations();

Assert.That(results.Count(), Is.EqualTo(3));
}

[Test]
public void AssignUserVariations_Updates_The_Cookie_Value_For_The_Requested_Variation_Only()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");

var results = ExperimentService().AssignUserVariations("vapBwUPvTEuGcEVEKThGCA~1");

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~1-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");
}

[Test]
public void AssignUserVariations_Does_Not_Change_The_Cookie_Value_For_The_Requested_Variation_When_The_Same()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");

var results = ExperimentService().AssignUserVariations("vapBwUPvTEuGcEVEKThGCA~0");

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");
}

[Test]
public void AssignUserVariations_Does_Not_Change_The_Cookie_Value_When_The_Requested_Experiment_Does_Not_Exist()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");

var results = ExperimentService().AssignUserVariations("vapBwUPvTEuGcEVEKThGCD~0");

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");
}

[Test]
public void AssignCurrentUserToVariation_Does_Not_Change_The_Cookie_Value_When_The_Requested_Variation_Does_Not_Exist()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");

var results = ExperimentService().AssignUserVariations("vapBwUPvTEuGcEVEKThGCA~3");

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~0-vapBwUPvTEuGcEVEKThGCB~1-vapBwUPvTEuGcEVEKThGCC~0");
}

[Test]
public void AssignUserVariations_Assigns_User_To_Experiments_Not_Currently_In_When_Updating_Variation()
{
_cookieService.Setup(s => s.GetValue<string>(It.IsAny<string>())).Returns("vapBwUPvTEuGcEVEKThGCA~0");

var results = ExperimentService().AssignUserVariations("vapBwUPvTEuGcEVEKThGCA~1");

AssertVariations(results, "vapBwUPvTEuGcEVEKThGCA~1-vapBwUPvTEuGcEVEKThGCB~0-vapBwUPvTEuGcEVEKThGCC~0");
}

private static void AssertVariations(IEnumerable<Variation> results, string variationsString)
{
var variations = variationsString.Split('-')
.Select(e => new Variation(0, int.Parse(e.Split('~')[1]), 1, true, "", e.Split('~')[0], false));

foreach (var variation in variations)
{
Assert.That(results.Any(r => r.ExperimentId == variation.ExperimentId && r.VariationNumber == variation.VariationNumber));
}
Assert.That(results.Count(), Is.EqualTo(variations.Count()));
}
}
}

Loading