I have a Carpenter class that does it's work using a Lathe and a Wood object.
class Carpenter
{
function Work()
{
$tool = new Lathe();
$material = new Wood();
$tool->Apply($material);
}
}
Lathe depends on an interface called Material, so I can easily unit test Lathe by giving it a fake Material in my unit test. Wood doesn't depend on anything, so it can also be easily tested.
interface Material {
// Various methods...
}
interface Tool {
function Apply(Material $m);
}
class Wood implements Material {
// Implementations of Material methods
}
class Lathe {
function Apply(Material $m) {
// Do processing
}
}
However, Carpenter depends on the concrete classes Lathe and Wood because it has to create instances of them. That means that as it currently stands, I cannot unit test the Work() method without inadvertantly bringing Lathe and Wood under test.
How should I change my design to unit test Carpenter?
There's a couple of different directions you can take here:
Use Constructor Injection and simply inject the tool and the material instances into the carpenter.
If injecting instances doesn't work for some reason (perhaps because you need to create new instances for every invocation of the Work method), you can inject Abstract Factories instead.
You can also use the Factory Method approach described by ctford, but that requires you to also create test-specific overrides to be able to unit test, and while that's a completely valid thing to do, it's just more work and in many cases the other alternatives are better and more flexible.
The key is to seperate object creation from object use in Carpenter.
class Carpenter
{
function getTool() {
return new Lathe();
}
function getMaterial() {
return new Wood();
}
function Work()
{
$tool = getTool();
$material = getMaterial();
$tool->Apply($material);
}
}
That way you can override the getTool() and getMaterial() methods in a TestCarpenter class and inject your own Material and Tool fakes.
The getTool() and getMaterial() methods can also be unit tested seperately.
Michael Feathers would call the getTool() and getMaterial() methods "seams", because they are points where fakes can be inserted without changing the surrounding code.
Related
I am trying to test an http server functionality and this is the first time I get exposed to the notions of mocks, stubs and fakes. I'm reading this book that it says mocking is describing the state of a complex object at a certain moment, like a snapshot, and testing the object as it is actually the real one. right? I understand this. what I don't understand is what is the point of writing unit tests and assertions of certain cases that we fully describe in the mock object. If we set the expectations of arguments and return values on the mocking object, and we test for those exact values, the test will always pass. Please correct me. I know I am missing something here.
Mocks, Stubs or anything similar serves as a substitute for the actual implementation.
The idea of mocks is basically to test a piece of code in a isolated environment, substituting the dependencies for a fake implementation. For example, in Java, suppose we want to test the PersonService#save method:
class PersonService {
PersonDAO personDAO;
// Set when the person was created and then save to DB
public void save(Person person) {
person.setCreationDate(new Date());
personDAO.save(person);
}
}
A good aproach is creating a unit test like this:
class PersonServiceTest {
// Class under test
PersonService personService;
// Mock
PersonDAO personDAOMock;
// Mocking the dependencies of personService.
// In this case, we mock the PersonDAO. Doing this
// we don't need the DB to test the code.
// We assume that personDAO will give us an expected result
// for each test.
public void setup() {
personService.setPersonDao(personDAOMock)
}
// This test will guarantee that the creationDate is defined
public void saveShouldDefineTheCreationDate() {
// Given a person
Person person = new Person();
person.setCreationDate(null);
// When the save method is called
personService.save(person);
// Then the creation date should be present
assert person.getCreationDate() != null;
}
}
In another words, you should use mock to substitute dependencies of your code to an "actor" that can be configured to return an expected result or assert some behaviours.
I'm mocking my repository correctly, but in cases like show() it either returns null so the view ends up crashing the test because of calling property on null object.
I'm guessing I'm supposed to mock the eloquent model returned but I find 2 issues:
What's the point of implementing repository pattern if I'm gonna end up mocking eloquent model anyway
How do you mock them correctly? The code below gives me an error.
$this->mockRepository->shouldReceive('find')
->once()
->with(1)
->andReturn(Mockery::mock('MyNamespace\MyModel)
// The view may call $book->title, so I'm guessing I have to mock
// that call and it's returned value, but this doesn't work as it says
// 'Undefined property: Mockery\CompositeExpectation::$title'
->shouldReceive('getAttribute')
->andReturn('')
);
Edit:
I'm trying to test the controller's actions as in:
$this->call('GET', 'books/1'); // will call Controller#show(1)
The thing is, at the end of the controller, it returns a view:
$book = Repo::find(1);
return view('books.show', compact('book'));
So, the the test case also runs view method and if no $book is mocked, it is null and crashes
So you're trying to unit test your controller to make sure that the right methods are called with the expected arguments. The controller-method fetches a model from the repo and passes it to the view. So we have to make sure that
the find()-method is called on the repo
the repo returns a model
the returned model is passed to the view
But first things first:
What's the point of implementing repository pattern if I'm gonna end up mocking eloquent model anyway?
It has many purposes besides (testable) consisten data access rules through different sources, (testable) centralized cache strategies, etc. In this case, you're not testing the repository and you actually don't even care what's returned, you're just interested that certain methods are called. So in combination with the concept of dependency injection you now have a powerful tool: You can just switch the actual instance of the repo with the mock.
So let's say your controller looks like this:
class BookController extends Controller {
protected $repo;
public function __construct(MyNamespace\BookRepository $repo)
{
$this->repo = $repo;
}
public function show()
{
$book = $this->repo->find(1);
return View::make('books.show', compact('book'));
}
}
So now, within your test you just mock the repo and bind it to the container:
public function testShowBook()
{
// no need to mock this, just make sure you pass something
// to the view that is (or acts like) a book
$book = new MyNamespace\Book;
$bookRepoMock = Mockery::mock('MyNamespace\BookRepository');
// make sure the repo is queried with 1
// and you want it to return the book instanciated above
$bookRepoMock->shouldReceive('find')
->once()
->with(1)
->andReturn($book);
// bind your mock to the container, so whenever an instance of
// MyNamespace\BookRepository is needed (like in your controller),
// the mock will be loaded.
$this->app->instance('MyNamespace\BookRepository', $bookRepoMock);
// now trigger the controller method
$response = $this->call('GET', 'books/1');
$this->assertEquals(200, $response->getStatusCode());
// check if the controller passed what was returned from the repo
// to the view
$this->assertViewHas('book', $book);
}
//EDIT in response to the comment:
Now, in the first line of your testShowBook() you instantiate a new Book, which I am assuming is a subclass of Eloquent\Model. Wouldn't that invalidate the whole deal of inversion of control[...]? since if you change ORM, you'd still have to change Book so that it wouldn't be class of Model
Well... yes and no. Yes, I've instantiated the model-class in the test directly, but model in this context doesn't necessarily mean instance of Eloquent\Model but more like the model in model-view-controller. Eloquent is only the ORM and has a class named Model that you inherit from, but the model-class as itself is just an entity of the business logic. It could extend Eloquent, it could extend Doctrine, or it could extend nothing at all.
In the end it's just a class that holds the data that you pull e.g. from a database, from an architecture point of view it is not aware of any ORM, it just contains data. A Book might have an author attribute, maybe even a getAuthor() method, but it doesn't really make sense for a book to have a save() or find() method. But it does if you're using Eloquent. And it's ok, because it's convenient, and in small project there's nothing wrong with accessing it directly. But it's the repository's (or the controller's) job to deal with a specific ORM, not the model's. The actual model is sort of the outcome of an ORM-interaction.
So yes, it might be a little confusing that the model seems so tightly bound to the ORM in Laravel, but, again, it's very convenient and perfectly fine for most projects. In fact, you won't even notice it unless you're using it directly in your application code (e.g. Book::where(...)->get();) and then decide to switch from Eloquent to something like Doctrine - this would obviously break your application. But if this is all encapsulated behind a repository, the rest of your application won't even notice when you switch between databases or even ORMs.
So, you're working with repositories, so only the eloquent-implementation of the repository should actually be aware that Book also extends Eloquent\Model and that it can call a save() method on it. The point is that it doesn't (=shouldn't) matter if Book extends Model or not, it should still be instantiable anywhere in your application, because within your business logic it's just a Book, i.e. a Plain Old PHP Object with some attributes and methods describing a book and not the strategies how to find or persist the object. That's what repositories are for.
But yes, the absolute clean way is to have a BookInterface and then bind it to a specific implementation. So it could all look like this:
Interfaces:
interface BookInterface
{
/**
* Get the ISBN.
*
* #return string
*/
public function getISBN();
}
interface BookRepositoryInterface()
{
/**
* Find a book by the given Id.
*
* #return null|BookInterface
*/
public function find($id);
}
Concrete implementations:
class Book extends Model implements BookInterface
{
public function getISBN()
{
return $this->isbn;
}
}
class EloquentBookRepository implements BookRepositoryInterface
{
protected $book;
public function __construct(Model $book)
{
$this->book = $book;
}
public function find($id)
{
return $this->book->find($id);
}
}
And then bind the interfaces to the desired implementations:
App::bind('BookInterface', function()
{
return new Book;
});
App::bind('BookRepositoryInterface', function()
{
return new EloquentBookRepository(new Book);
});
It doesn't matter if Book extends Model or anything else, as long as it implements the BookInterface, it is a Book. That's why I bravely instantiated a new Book in the test. Because it doesn't matter if you change the ORM, it only matters if you have several implementations of the BookInterface, but that's not very likely (sensible?), I guess. But just to play it safe, now that it's bound to the IoC-Container, you can instantiate it like this in the test:
$book = $this->app->make('BookInterface');
which will return an instance of whatever implementation of Book you're currently using.
So, for better testability
Code to interfaces rather than concrete classes
Use Laravel's IoC-Container to bind interfaces to concrete implementations (including mocks)
Use dependency injection
I hope that makes sense.
Update
I want to find out if the factory should be unit tested based on its usage of method, because if I want to test it, I need to register type using IoC container, Unity used in my case. If I mock the factory, it is not actuall testing the factory method.
Below is a factory class that creates instances of type based on its parameter.
public class CarFactory
{
public ICar CreateCar(string CarType)
{
ICar Car;
switch (CarType)
{
case RepositoryType.Car1:
Car = Ioc.ContainerWrapper.Resolve<Car1>();
break;
case RepositoryType.Car2:
Car = Ioc.ContainerWrapper.Resolve<Car2>();
break;
default:
Car = Ioc.ContainerWrapper.Resolve<Car3>();
break;
}
return Car;
}
}
class Car1
{
private readonly IRepository1 _IRepository1;
public Car1(IRepository1 repository1)
{
_IRepository1 = repository1;
}
}
You are on the right path, from my point of view.
The purpose of a factory is to create an object so they are allowed to call the Service Locator (Yes, you are still using the Service Locator anti-pattern but you are moving it from your domain to just a factory)
Reference:
https://github.com/ninject/ninject.extensions.factory/wiki
Back to your original question, I believe you should test your factory to guarantee that you are getting the correct object depending on the parameter used, there is a debate on if this is a unit test or any kind of integration test, in my opinion it is still a unit test following this Martin Fowler article:
http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting
This is how I would test it:
IRisk myRisk = new RiskFactory().CreateGoldRisk("Risk1");
myRisk.Should().NotBeNull().And.BeOfType<Risk1>();
You'll have to prepare container suitable for this test (that is, having Risk classes resolvable). With your current implementation there's not much else you can do.
Of course, this raises questions whether this is unit test anymore or not, but we can assume unity is well tested and will work (ie. assuming it won't be a possible failing point of unit test).
That's natural drawback of having strong dependency to IoC container in your code. One might argue you can wrap container with your custom abstraction and ... and inject it? This doesn't feel right and I'm yet to see somebody doing that.
For related problem, see this question.
Can't you just test that like this:
[SetUp]
public void setup()
{
// code here to set up the container....
}
[Test]
public void example_test()
{
var factory = new RiskFactory();
var risk = factory.CreateGoldRisk("xxxx");
Assert.True(risk is Risk1);
}
On a side note. You have an IOC container. A container wrapper which is then wrapped in a factory. Your architecture probably has too many layers of abstraction..
I'm getting started unit testing my PHP application with PHPUnit. I understand that it's important for unit tests to run in isolation so you know where to look when a test fails. One thing I am struggling to understand is how to test subclasses in isolation from their parent. For example, most of my models extend a "base model" which has most of the features that a model should have.
<?php
class BaseModel
{
public function save($data) {
// write $data to the database
$dbAdapter->save($data);
}
}
class RegularModel extends BaseModel
{
public function save($data) {
// clean up $data before passing it to parent
if (isset($data['foo'])) {
unset($data['foo']);
$data['bar'] = 'foo';
}
parent::save($data);
}
}
# Unit Test
class RegularModelTest extends PHPUnit_Framework_TestCase
{
public function testSaveMethodCallsParent() {
$data = array('foo' => 'yes');
$model = new RegularModel();
$model->save($data);
// assert parent received data correctly
}
}
I'm not sure how to test my RegularModel without calling a bunch of unnecessary code. I'm also doing some autoloading so when it calls save on the parent, it will actually try to save to the test database. I'd rather mock this out since I don't care about whether or not it actually writes to the database when I'm testing my RegularModel only when I am testing my BaseModel. Or am I thinking about this all wrong? What do you recommend when it comes to testing situations like this?
Your best bet is to mock the $dbAdapter when testing RegularModel. While you are still executing the parent class's code, that code really is part of the RegularModel unit due to the is-a relationship.
The only way around it would be to provide a different implementation of BaseModel. You can either run these tests in a separate process or use Runkit to swap in the other implementation at runtime. Both of these have drawbacks--complexity, performance degredation, and instability--that come at too high a price in my view.
Subclasses are, by definition, tightly coupled to their superclasses so practically speaking there is no way to test a subclass in isolation.
However, if the superclass has an extensive test suite that passes then you normally can be confident that you can test the subclass by just covering the methods implemented in the subclass. The superclass test suite covers the superclass functionality.
I am interested in the best way to write unit tests for a class whose public API involves some kind of a flow, for example:
public class PaginatedWriter {
public void AppendLine(string line) { ... }
public IEnumerable<string> GetPages() { ... }
public int LinesPerPage { get; private set; }
}
This class paginates text lines into the given number of lines per page. In order to test this class, we may have something like:
public void AppendLine_EmptyLine_AddsEmptyLine() { ... }
public void AppendLine_NonemptyLine_AddsLine() { ... }
public void GetPages_ReturnsPages() {
writer.AppendLine("abc");
writer.AppendLine("def");
var output = writer.GetPages();
...
}
Now, my question is: is it OK to make calls to AppendLine() in the last test method, even though we are testing the GetPages() method?
I know one solution in such situations is to make AppendLine() virtual and override it but the problem is that AppendLine() manipulates internal state which I don't think should be the business of the unit test.
The way I see it is that tests usually follow a pattern like 'setup - operate - check - teardown'.
I concentrate most of the common setup and teardown in the respective functions.
But for test specific setup and teardown it is part of the test method.
I see nothing wrong with preparing the state of the Object Under Test using method calls of that object. In OOP I would not try to decouple the state from the operations since the paradigm goes to great lengths to unify them and if possible even hide the state. In my view the unit under test is the Class - state and methods.
I do make visual distinction in the code by separating the setup block from the operate block and the verify block with an empty line.
Yes, I'd say that's absolutely fine.
Test in whatever way you find practical. I find there's rather too much dogma around testing exactly one thing in each test method. That's a lovely ideal, but sometimes it's just not nearly as practical as slightly less pure alternatives.