Posted
almost 11 years
ago
by
[email protected] (Nikos Baxevanis)
The complete list can be found here.
AutoFixture.Auto[Moq/RhinoMocks/FakeItEasy/NSubstitute]
What is AutoFixture AutoMoq?
Differences between Moq and AutoFixture
How to verify a method was called
Using verify to confirm expected parameter
... [More]
values in Moq’s Mock class
Creating a hybrid of a Mock and an Anonymous object using Moq and AutoFixture
Test a class that takes a Factory
Repetitive code in Unit Tests
Integration Test an application using Castle Windsor
AutoFixture as an Auto-mocking Container vs Auto-mocking differences
Create an instance of a type that contains properties with an interface type
How to return results from injected services in an instantiated object
Mocked methods returning a Frozen instance
Proper use of AutoFixture AutoMoq
AutoData Theories with AutoFixture using manual fakes
How to express a Unit Test declaratively?
Imperative to Declarative Unit Test
Auto-mocking using FakeItEasy
Auto-mocking the SUT
AutoFixture.Idioms
When I pass input to the parameter x of a method, supplying the other parameters anonymously, the result is ..
Simplifing Unit Testing DDD Value objects Equality
Verify invalid constructor parameters
AutoFixture.Xunit
Integration Test an application using Castle Windsor
AutoFixture with ‘weak’ types
Proxies in data integration tests
AutoFixture with derived types
Mocked methods returning a Frozen instance
Proper use of AutoFixture AutoMoq
How to express a Unit Test declaratively
Imperative to Declarative Unit Test
Auto-mocking using FakeItEasy
What are the principles behind AutoFixture’s declarative way of setting up a fixture?
PropertyDataAttribute and heterogeneous parameters
Customizations
How to create a list of types with predefined values
How to set a private setter of an Automatic Property by calling a method
Generate anonymous number for string property
Execute a delegate at object creation time
How to limit the length of generated strings
Integration Test an application using Castle Windsor
Selecting the greediest constructor by default
How to call a base method for all instances of classes that inherit from it
Creating ASP.NET MVC Controller instances without setting property values
How to generate objects when there are multiple paths to a child entity
AutoFixture with ‘weak’ types
Proxies in data integration tests
Supplying values for inherited properties
Mocked methods returning a Frozen instance
Creating ASP.NET MVC Controller instances
Using Customize with seeded property
PropertyTypeOmitter (System.Runtime.Serialization.ExtensionDataObject example)
Customizing constructor parameter values
Ignore virtual properties
Creating an ISpecimenBuilder for a type
Imperative to Declarative Unit Test
Freeze, Inject
How to verify a method was called
How to Freeze a Null Reference
AutoFixture with ‘weak’ types
AutoFixture with derived types
Auto-mocking using FakeItEasy
Supply a known value for one constructor parameter
Likeness, Likeness as Resemblance
Compare only matching properties
Likeness applied to sequences
Custom Assertion
Using verify to confirm expected parameter values in Moq’s Mock class
Likeness applied to sequences
How to use ‘EqualsWhen’
Refactoring
Minimizing the Arrange phase
Repetitive code in Unit Tests
Imperative to Declarative Unit Test
[Less]
|
Posted
almost 11 years
ago
by
[email protected] (Nikos Baxevanis)
The complete list can be found here.
AutoFixture.Auto[Moq/RhinoMocks/FakeItEasy/NSubstitute]
What is AutoFixture AutoMoq?
Differences between Moq and AutoFixture
How to verify a method was called
Using verify to confirm expected parameter values
... [More]
in Moq’s Mock class
Creating a hybrid of a Mock and an Anonymous object using Moq and AutoFixture
Test a class that takes a Factory
Repetitive code in Unit Tests
Integration Test an application using Castle Windsor
AutoFixture as an Auto-mocking Container vs Auto-mocking differences
Create an instance of a type that contains properties with an interface type
How to return results from injected services in an instantiated object
Mocked methods returning a Frozen instance
Proper use of AutoFixture AutoMoq
AutoData Theories with AutoFixture using manual fakes
How to express a Unit Test declaratively?
Imperative to Declarative Unit Test
Auto-mocking using FakeItEasy
Auto-mocking the SUT
AutoFixture.Idioms
When I pass input to the parameter x of a method, supplying the other parameters anonymously, the result is ..
Simplifing Unit Testing DDD Value objects Equality
Verify invalid constructor parameters
AutoFixture.Xunit
Integration Test an application using Castle Windsor
AutoFixture with ‘weak’ types
Proxies in data integration tests
AutoFixture with derived types
Mocked methods returning a Frozen instance
Proper use of AutoFixture AutoMoq
How to express a Unit Test declaratively
Imperative to Declarative Unit Test
Auto-mocking using FakeItEasy
What are the principles behind AutoFixture’s declarative way of setting up a fixture?
PropertyDataAttribute and heterogeneous parameters
Customizations
How to create a list of types with predefined values
How to set a private setter of an Automatic Property by calling a method
Generate anonymous number for string property
Execute a delegate at object creation time
How to limit the length of generated strings
Integration Test an application using Castle Windsor
Selecting the greediest constructor by default
How to call a base method for all instances of classes that inherit from it
Creating ASP.NET MVC Controller instances without setting property values
How to generate objects when there are multiple paths to a child entity
AutoFixture with ‘weak’ types
Proxies in data integration tests
Supplying values for inherited properties
Mocked methods returning a Frozen instance
Creating ASP.NET MVC Controller instances
Using Customize with seeded property
PropertyTypeOmitter (System.Runtime.Serialization.ExtensionDataObject example)
Customizing constructor parameter values
Ignore virtual properties
Creating an ISpecimenBuilder for a type
Imperative to Declarative Unit Test
Freeze, Inject
How to verify a method was called
How to Freeze a Null Reference
AutoFixture with ‘weak’ types
AutoFixture with derived types
Auto-mocking using FakeItEasy
Supply a known value for one constructor parameter
Likeness, Likeness as Resemblance
Compare only matching properties
Likeness applied to sequences
Custom Assertion
Using verify to confirm expected parameter values in Moq’s Mock class
Likeness applied to sequences
How to use ‘EqualsWhen’
Refactoring
Minimizing the Arrange phase
Repetitive code in Unit Tests
Imperative to Declarative Unit Test
[Less]
|
Posted
about 11 years
ago
by
Enrico Campidoglio
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction: A customization is a group of settings that, when applied to a given Fixture object, control the way AutoFixture [...]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count() - 1);
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection recipes;
public Cookbook(IEnumerable recipes)
{
this.recipes = new List(recipes);
}
public IEnumerable FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable Ingredients;
public Recipe(IEnumerable ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count() - 1);
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<Ingredient> sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<Ingredient>();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection<Recipe> recipes;
public Cookbook(IEnumerable<Recipe> recipes)
{
this.recipes = new List<Recipe>(recipes);
}
public IEnumerable<Recipe> FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable<Ingredient> Ingredients;
public Recipe(IEnumerable<Ingredient> ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence<T> : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<T> sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<T>();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence<Ingredient>())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence<Ingredient>());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<Ingredient> sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<Ingredient>();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection<Recipe> recipes;
public Cookbook(IEnumerable<Recipe> recipes)
{
this.recipes = new List<Recipe>(recipes);
}
public IEnumerable<Recipe> FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable<Ingredient> Ingredients;
public Recipe(IEnumerable<Ingredient> ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence<T> : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<T> sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<T>();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence<Ingredient>())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence<Ingredient>());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<Ingredient> sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<Ingredient>();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection<Recipe> recipes;
public Cookbook(IEnumerable<Recipe> recipes)
{
this.recipes = new List<Recipe>(recipes);
}
public IEnumerable<Recipe> FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable<Ingredient> Ingredients;
public Recipe(IEnumerable<Ingredient> ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence<T> : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<T> sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<T>();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence<Ingredient>())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence<Ingredient>());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<Ingredient> sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<Ingredient>();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection<Recipe> recipes;
public Cookbook(IEnumerable<Recipe> recipes)
{
this.recipes = new List<Recipe>(recipes);
}
public IEnumerable<Recipe> FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable<Ingredient> Ingredients;
public Recipe(IEnumerable<Ingredient> ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence<T> : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<T> sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<T>();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence<Ingredient>())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence<Ingredient>());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
If you’ve been using AutoFixture in your tests for more than a while, chances are you’ve already come across the concept of customizations. If you’re not familiar with it, let me give you a quick introduction:
A customization is a group of
... [More]
settings that, when applied to a given Fixture object, control the way AutoFixture will create instances for the types requested through that Fixture.
At this point you might find yourself feeling an irresistible urge to know everything there’s to know about customizations. If that’s the case, don’t worry. There are a few resources online where you learn more about them. For example, I wrote about how to take advantage of customizations to group together test data related to specific scenarios.
In this post I’m going to talk about something different which, in a sense, is quite the opposite of that: how to write general-purpose customizations.
A (user) story about cooking
It’s hard to talk about test data without a bit of context. So, for the sake of this post, I thought we would pretend to be working on a somewhat realistic project. The system we’re going to build is an online catalogue of food recipies. The domain, at the very basic level, consists of three concepts:
Cookbook
Recipes
Ingredients
Now, let’s imagine that in our backlog of requirements we have one where the user wishes to be able to search for recepies that contain a specific set of ingredients. Or in other words:
As a foodie, I want to know which recipes I can prepare with the ingredients I have,
so that I can get the best value for my groceries.
From the tests…
As usual, we start out by translating the requirement at hand into a set of acceptance tests. In order do that, we need to tell AutoFixture how we’d like the test data for our domain model to be generated.
For this particular scenario, we need every Ingredient created in the test fixture to be randomly chosen from a fixed pool of objects. That way we can ensure that all recepies in the cookbook will be made up of the same set of ingredients.
Here’s how such a customization would look like:
public class RandomIngredientsFromFixedSequence : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<Ingredient> sequence;
public void Customize(IFixture fixture)
{
InitializeIngredientSequence(fixture);
fixture.Register(PickRandomIngredientFromSequence);
}
private void InitializeIngredientSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<Ingredient>();
}
private Ingredient PickRandomIngredientFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Here we’re creating a pool of ingredients and telling AutoFixture to randomly pick one of those every time it needs to create an Ingredient object by using the Fixture.Register method.
Since we’ll be using Xunit as our test runner, you can take advantage of the AutoFixture Data Theories to keep our tests succinct by using AutoFixture in a declarative fashion. In order to do so, we need to write an xUnit Data Theory attribute that tells AutoFixture to use our new customization:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomIngredientsFromFixedSequence())))
{
}
}
If you prefer to use AutoFixture directly in your tests, the imperative equivalent of the above is:
var fixture = new Fixture();
fixture.Customize(new RandomIngredientsFromFixedSequence());
At this point, we can finally start writing the acceptance tests to satisfy our original requirement:
public class When_searching_for_recipies_by_ingredients
{
[Theory, CookbookAutoData]
public void Should_only_return_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient)
{
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.True(recipes.All(r => r.Ingredients.Contains(ingredient)));
}
[Theory, CookbookAutoData]
public void Should_include_new_recipes_with_a_specific_ingredient(
Cookbook sut,
Ingredient ingredient,
Recipe recipeWithIngredient)
{
// Given
sut.AddRecipe(recipeWithIngredient);
// When
var recipes = sut.FindRecipies(ingredient);
// Then
Assert.Contains(recipeWithIngredient, recipes);
}
}
Notice that during these tests AutoFixture will have to create Ingredient objects in a couple of different ways:
indirectly when constructing Recipe objects associated to a Cookbook
directly when providing arguments for the test parameters
As far as AutoFixture is concerned, it doesn’t really matter which code path leads to the creation of ingredients. The algorithm provided by the RandomIngredientsFromFixedSequence customization will apply in all situations.
…to the implementation
After a couple of Red-Green-Refactor cycles spawned from the above tests, it’s not completely unlikely that we might end up with some production code similar to this:
// Cookbook.cs
public class Cookbook
{
private readonly ICollection<Recipe> recipes;
public Cookbook(IEnumerable<Recipe> recipes)
{
this.recipes = new List<Recipe>(recipes);
}
public IEnumerable<Recipe> FindRecipies(params Ingredient[] ingredients)
{
return recipes.Where(r => r.Ingredients.Intersect(ingredients).Any());
}
public void AddRecipe(Recipe recipe)
{
this.recipes.Add(recipe);
}
}
// Recipe.cs
public class Recipe
{
public readonly IEnumerable<Ingredient> Ingredients;
public Recipe(IEnumerable<Ingredient> ingredients)
{
this.Ingredients = ingredients;
}
}
// Ingredient.cs
public class Ingredient
{
public readonly string Name;
public Ingredient(string name)
{
this.Name = name;
}
}
Nice and simple. But let’s not stop here. It’s time to take it a bit further.
An opportunity for generalization
Given the fact that we started working from a very concrete requirement, it’s only natural that the RandomIngredientsFromFixedSequence customization we came up at with encapsulates a behavior that is specific to the scenario at hand. However, if we take a closer look we might notice the following:
The only part of the algorithm that is specific to the original scenario is the type of the objects being created. The rest can easily be applied whenever you want to create objects that are picked at random from a predefined pool.
An opportunity for writing a general-purpose customization has just presented itself. We can’t let it slip.
Let’s see what happens if we extract the Ingredient type into a generic argument and remove all references to the word “ingredient”:
public class RandomFromFixedSequence<T> : ICustomization
{
private readonly Random randomizer = new Random();
private IEnumerable<T> sequence;
public void Customize(IFixture fixture)
{
InitializeSequence(fixture);
fixture.Register(PickRandomItemFromSequence);
}
private void InitializeSequence(IFixture fixture)
{
this.sequence = fixture.CreateMany<T>();
}
private T PickRandomItemFromSequence()
{
var randomIndex = this.randomizer.Next(0, sequence.Count());
return sequence.ElementAt(randomIndex);
}
}
Voilà. We just turned our scenario-specific customization into a pluggable algorithm that changes the way objects of any type are going to be generated by AutoFixture. In this case the algorithm will create items by picking them at random from a fixed sequence of T.
The CookbookAutoDataAttribute can easily changed to use the general-purpose version of the customization by closing the generic argument with the Ingredient type:
public class CookbookAutoDataAttribute : AutoDataAttribute
{
public CookbookAutoDataAttribute()
: base(new Fixture().Customize(
new RandomFromFixedSequence<Ingredient>())))
{
}
}
The same is true if you’re using AutoFixture imperatively:
var fixture = new Fixture();
fixture.Customize(new RandomFromFixedSequence<Ingredient>());
Wrapping up
As I said before, customizations are a great way to set up test data for a specific scenario. Sometimes these configurations turn out to be useful in more than just one situation.
When such opportunity arises, it’s often a good idea to separate out the parts that are specific to a particular context and turn them into parameters. This allows the customization to become a reusable strategy for controlling AutoFixture’s behavior across entire test suites. [Less]
|
Posted
about 11 years
ago
by
Mark Seemann
Announcing the general release of AutoFixture 3.
AutoFixture 3 has been under way in a long time, but after about of a month of limited beta/CTP/whatever trial, I've decided to release it as the 'official' current version of AutoFixture. Since
... [More]
AutoFixture uses Semantic Versioning, the shift from 2.16.2 to 3.0.0 (actually, 3.0.1) signifies breaking changes. While we've made an effort to make the impact of these breaking changes as light as possible, it's worth noting that it's probably not a good idea upgrading to AutoFixture 3 five minutes before you are heading out of the door for the weekend.
Read more about what's new and what has changed in the release notes.
Thank you to everyone who helped make this happen. There have been a lot of people contributing, providing feedback, asking intelligent questions, etc. but I particularly want to thank Nikos Baxevanis for all the work he's been putting into AutoFixture. Conversely, as is usual in such circumstances, all mistakes are my responsibility.
What's new?
When you read the release notes, you may find the amount of new features is quite unimpressive. This is because we've been continually releasing new features under the major version number of 2, in a sort of Continuous Delivery process. Because of the way Semantic Versioning works, breaking changes must be signaled by increasing the major version number, so while we could release new features as they became available, I thought it better to bundle up breaking changes into a bigger release; if not, we'd be looking at AutoFixture 7 or 8 now... And as I'm writing this, I notice that my installation of Chrome is on major version 25, so maybe it's just something I need to get over.
Personal release notes
One thing that the release notes don't explicitly mention, but I'd like to talk about a little is that AutoFixture 3 is running on an extensively rebuilt kernel. One of the visions I had for AutoFixture 3 was that the kernel structure should be immutable, and it should be as easy to take away functionality than it is to add custom functionality. Taking away functionality was really hard (or perhaps impossible) in AutoFixture 2, and that put a constraint on what you could do with AutoFixture. It also put a constraint on how opinionated its defaults could be.
The AutoFixture 3 kernel makes it possible to take away functionality, as well as add new functionality. At the moment, you have to understand the underlying kernel to figure out how to do that, but in the future I plan to surface an API for that as well.
In any case, I find it interesting that using real SOLID principles, I was able to develop the new kernel in parallel with the version 2 kernel. Partly because I was very conscious of not introducing breaking changes, but also because I follow the Open/Closed Principle, I rarely had to touch existing code, but could build the new parts of the kernel in parallel. Even more interestingly, I think, is the fact that once that work was done, replacing the kernel turned out to be a point release. This happened late November 2012 - more than three months ago. If you've been using AutoFixture 2.14.1 or a more recent version, you've already been using the AutoFixture 3 kernel for some time.
The reason I find this interesting is that sometimes casual readers of my blog express skepticism over whether the principles I talk about can be applied in 'realistic' settings. According to Ohloh, AutoFixture has a total of 197,935 lines of code, and I consider that a fair sized code base. I practice what I preach. QED.
This blog is totally free, but if you like it, please buy me a cup of coffee. [Less]
|